From d6f8efae246db921b8d52fc1f86bbf667931dd93 Mon Sep 17 00:00:00 2001 From: Andrew Productengine Date: Mon, 6 Dec 2010 18:05:44 +0200 Subject: STORM-34 FIXED Saving of user's favorites into file and showing them in "Start at" combobox on login screen was implemented. Implementation details: - File is saved on exit from viewer and not immediately on changes as was written in spec. It is done to make this file consistent with favorites order: order of favorites is saved on exit, so if favorites info is saved in other moment earlier, crashing viewer or other unexpected way of finishing its work (i.e. via Windows task bar) would cause inconsistence between favorites order saved per account and one from this new file. - File is saved in user_settings\stored_favorites.xml. - If you uncheck the option in Preferences and press OK, the file gets immediately deleted (according to spec). Issues that require further changes: - Currently only favorites of last logged in user are shown in login screen. Showing favorites of multiple users will be implemented later when design for it is approved by Esbee. - Preference is now global for all users, because design states it may be changed before login, and we don't have account info at the moment. But it doesn't seem to be a good idea, so changes in design are needed. - Currently the way of retrieving SLURLs needs optimization in a separate ticket. More detailed design approved by Esbee is needed to develop it further, perhaps in new tickets. --- indra/newview/app_settings/settings.xml | 11 +++ indra/newview/llfavoritesbar.cpp | 10 ++ indra/newview/llfloaterpreference.cpp | 21 +++++ indra/newview/llfloaterpreference.h | 3 + indra/newview/llpanellogin.cpp | 36 +++++++- indra/newview/llpanellogin.h | 2 + indra/newview/llviewerinventory.cpp | 102 +++++++++++++++++++++ .../newview/skins/default/xui/en/notifications.xml | 10 ++ .../default/xui/en/panel_preferences_privacy.xml | 13 ++- 9 files changed, 205 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 402a0e85c4..871053782b 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12332,5 +12332,16 @@ Value name + ShowFavoritesOnLogin + + Comment + Determines whether favorites of last logged in user will be saved on exit from viewer and shown on login screen + Persist + 1 + Type + Boolean + Value + 0 + diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index a1ba370c26..4f87221065 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -606,6 +606,16 @@ void LLFavoritesBarCtrl::changed(U32 mask) } else { + LLInventoryModel::item_array_t items; + LLInventoryModel::cat_array_t cats; + LLIsType is_type(LLAssetType::AT_LANDMARK); + gInventory.collectDescendentsIf(mFavoriteFolderId, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + S32 sortField = 0; + for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) + { + (*i)->setSortField(++sortField); + } updateButtons(); } } diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 6a7b5171b5..6500aefb10 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -330,6 +330,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); gSavedSettings.getControl("UseDisplayNames")->getCommitSignal()->connect(boost::bind(&handleDisplayNamesOptionChanged, _2)); + + mFavoritesFileMayExist = gSavedSettings.getBOOL("ShowFavoritesOnLogin"); } BOOL LLFloaterPreference::postBuild() @@ -489,6 +491,13 @@ void LLFloaterPreference::apply() updateDoubleClickSettings(); mDoubleClickActionDirty = false; } + + if (mFavoritesFileMayExist && !gSavedSettings.getBOOL("ShowFavoritesOnLogin")) + { + mFavoritesFileMayExist = false; + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLFile::remove(filename); + } } void LLFloaterPreference::cancel() @@ -1491,6 +1500,10 @@ BOOL LLPanelPreference::postBuild() { getChild("voice_call_friends_only_check")->setCommitCallback(boost::bind(&showFriendsOnlyWarning, _1, _2)); } + if (hasChild("favorites_on_login_check")) + { + getChild("favorites_on_login_check")->setCommitCallback(boost::bind(&showFavoritesOnLoginWarning, _1, _2)); + } // Panel Advanced if (hasChild("modifier_combo")) @@ -1558,6 +1571,14 @@ void LLPanelPreference::showFriendsOnlyWarning(LLUICtrl* checkbox, const LLSD& v } } +void LLPanelPreference::showFavoritesOnLoginWarning(LLUICtrl* checkbox, const LLSD& value) +{ + if (checkbox && checkbox->getValue()) + { + LLNotificationsUtil::add("FavoritesOnLogin"); + } +} + void LLPanelPreference::cancel() { for (control_values_map_t::iterator iter = mSavedValues.begin(); diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index bb871e7e25..c95a2472a7 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -162,6 +162,7 @@ private: bool mLanguageChanged; bool mOriginalHideOnlineStatus; + bool mFavoritesFileMayExist; std::string mDirectoryVisibility; }; @@ -183,6 +184,8 @@ public: private: //for "Only friends and groups can call or IM me" static void showFriendsOnlyWarning(LLUICtrl*, const LLSD&); + //for "Show my Favorite Landmarks at Login" + static void showFavoritesOnLoginWarning(LLUICtrl* checkbox, const LLSD& value); typedef std::map control_values_map_t; control_values_map_t mSavedValues; diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index cf567fb208..16ea303c77 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -262,11 +262,45 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, gResponsePtr = LLIamHereLogin::build( this ); LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); - + + // Show last logged in user favorites in "Start at" combo if corresponding option is enabled. + if (gSavedSettings.getBOOL("ShowFavoritesOnLogin")) + { + addFavoritesToStartLocation(); + } + updateLocationCombo(false); } +void LLPanelLogin::addFavoritesToStartLocation() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + LLComboBox* combo = getChild("start_location_combo"); + combo->addSeparator(); + LLSDSerialize::fromXML(fav_llsd, file); + for (LLSD::map_const_iterator iter = fav_llsd.beginMap(); + iter != fav_llsd.endMap(); ++iter) + { + LLSD user_llsd = iter->second; + for (LLSD::array_const_iterator iter1 = user_llsd.beginArray(); + iter1 != user_llsd.endArray(); ++iter1) + { + std::string label = (*iter1)["name"].asString(); + std::string value = (*iter1)["slurl"].asString(); + if(label != "" && value != "") + { + combo->add(label, value); + } + } + + } +} + // force the size to be correct (XML doesn't seem to be sufficient to do this) // (with some padding so the other login screen doesn't show through) void LLPanelLogin::reshapeBrowser() diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 83e76a308b..8a8888a053 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -85,6 +85,8 @@ public: private: friend class LLPanelLoginListener; void reshapeBrowser(); + // adds favorites of last logged in user from file to "Start at" combobox. + void addFavoritesToStartLocation(); static void onClickConnect(void*); static void onClickNewAccount(void*); // static bool newAccountAlertCallback(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 7dbaa4cf92..941e81d36f 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -48,6 +48,7 @@ #include "llinventorybridge.h" #include "llinventorypanel.h" #include "llfloaterinventory.h" +#include "lllandmarkactions.h" #include "llviewerassettype.h" #include "llviewerregion.h" @@ -59,6 +60,8 @@ #include "llcommandhandler.h" #include "llviewermessage.h" #include "llsidepanelappearance.h" +#include "llavatarnamecache.h" +#include "lllogininstance.h" ///---------------------------------------------------------------------------- /// Helper class to store special inventory item names and their localized values. @@ -1414,6 +1417,8 @@ public: S32 getSortIndex(const LLUUID& inv_item_id); void removeSortIndex(const LLUUID& inv_item_id); + void getSLURL(const LLUUID& asset_id); + /** * Implementation of LLDestroyClass. Calls cleanup() instance method. * @@ -1440,9 +1445,17 @@ private: void load(); void save(); + void saveFavoritesSLURLs(); + + void onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark); + void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); + typedef std::map sort_index_map_t; sort_index_map_t mSortIndexes; + typedef std::map slurls_map_t; + slurls_map_t mSLURLs; + bool mIsDirty; struct IsNotInFavorites @@ -1497,10 +1510,27 @@ void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) mIsDirty = true; } +void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) +{ + slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); + if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached + + LLLandmark* lm = gLandmarkList.getAsset(asset_id, + boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); + if (lm) + { + onLandmarkLoaded(asset_id, lm); + } +} + // static void LLFavoritesOrderStorage::destroyClass() { LLFavoritesOrderStorage::instance().cleanup(); + if (gSavedSettings.getBOOL("ShowFavoritesOnLogin")) + { + LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); + } } void LLFavoritesOrderStorage::load() @@ -1523,6 +1553,77 @@ void LLFavoritesOrderStorage::load() } } +void LLFavoritesOrderStorage::saveFavoritesSLURLs() +{ + // Do not change the file if we are not logged in yet. + if (!LLLoginInstance::getInstance()->authSuccess()) return; + + std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); + if (user_dir.empty()) return; + + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + LLSD user_llsd; + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) + { + LLSD value; + value["name"] = (*it)->getName(); + value["asset_id"] = (*it)->getAssetUUID(); + + slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); + if (slurl_iter != mSLURLs.end()) + { + value["slurl"] = slurl_iter->second; + } + else + { + llwarns << "Fetching SLURLs for \"Favorites\" is not complete!" << llendl; + return; + } + + user_llsd[(*it)->getSortField()] = value; + } + + LLSD fav_llsd; + // this level in LLSD is not needed now and is just a stub, but will be needed later when implementing save of multiple users favorites in one file. + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + fav_llsd[av_name.getLegacyName()] = user_llsd; + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, file); +} + +void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) +{ + if (!landmark) return; + + LLVector3d pos_global; + if (!landmark->getGlobalPos(pos_global)) + { + // If global position was unknown on first getGlobalPos() call + // it should be set for the subsequent calls. + landmark->getGlobalPos(pos_global); + } + + if (!pos_global.isExactlyZero()) + { + LLLandmarkActions::getSLURLfromPosGlobal(pos_global, + boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); + } +} + +void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) +{ + mSLURLs[asset_id] = slurl; +} + void LLFavoritesOrderStorage::save() { // nothing to save if clean @@ -1579,6 +1680,7 @@ S32 LLViewerInventoryItem::getSortField() const void LLViewerInventoryItem::setSortField(S32 sortField) { LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); + LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID); } const LLPermissions& LLViewerInventoryItem::getPermissions() const diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 60b876d163..34ccecce09 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -251,6 +251,16 @@ Save all changes to clothing/body parts? yestext="OK"/> + + Note: When you turn on this option, anyone who uses this computer can see your list of favorite locations. + + + + Chat Logs: @@ -170,7 +179,7 @@ layout="topleft" left="30" name="block_list" - top_pad="35" + top_pad="28" width="145"> -- cgit v1.2.3 From 32750132db47eb335c56f6c880902cf7195e1825 Mon Sep 17 00:00:00 2001 From: Andrew Productengine Date: Thu, 9 Dec 2010 19:54:40 +0200 Subject: STORM-34 ADDITIONAL_FIX Implemented storing of multi-user favorites and showing them on login screen. - Changed the way SLURLs are cached a little, because previous one introduced problems with theit order. - Also allowed saving of favorites to disk even if not all of them received SLURL info - this is done to avoid favorites not saving when there is at least one "dead" landmark among them. - "Username" field on login screen is now not a lineeditor, but combobox (to enable autocompletion), but without button (Esbee asked for this in ticket for security reasons, and perhaps for visual consistency). - Elements of this combobox are names of users whose favorites we have saved in file. - Contents of "Start at:" combobox are changed depending on changes in "Username"- if username is present in favorites file, favorites for this user are added there. - New callback was added to LLCombobox and used in this fix, because present ones weren't enough to easily track changes in text entry. --- indra/llui/llcombobox.cpp | 9 +++ indra/llui/llcombobox.h | 5 +- indra/newview/llfavoritesbar.cpp | 3 +- indra/newview/llpanellogin.cpp | 68 ++++++++++++++++------ indra/newview/llpanellogin.h | 2 +- indra/newview/llviewerinventory.cpp | 22 ++++--- indra/newview/llviewerinventory.h | 1 + indra/newview/skins/default/xui/en/panel_login.xml | 21 ++++--- 8 files changed, 93 insertions(+), 38 deletions(-) (limited to 'indra') diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 70014fe4f5..9f32ade280 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -94,6 +94,7 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p) mMaxChars(p.max_chars), mPrearrangeCallback(p.prearrange_callback()), mTextEntryCallback(p.text_entry_callback()), + mTextChangedCallback(p.text_changed_callback()), mListPosition(p.list_position), mLastSelectedIndex(-1), mLabel(p.label) @@ -833,6 +834,10 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) mList->deselectAllItems(); mLastSelectedIndex = -1; } + if (mTextChangedCallback != NULL) + { + (mTextChangedCallback)(line_editor, LLSD()); + } return; } @@ -877,6 +882,10 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) // RN: presumably text entry updateSelection(); } + if (mTextChangedCallback != NULL) + { + (mTextChangedCallback)(line_editor, LLSD()); + } } void LLComboBox::updateSelection() diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index 5f0e4a6843..74d64269bd 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -73,7 +73,8 @@ public: allow_new_values; Optional max_chars; Optional prearrange_callback, - text_entry_callback; + text_entry_callback, + text_changed_callback; Optional list_position; @@ -190,6 +191,7 @@ public: void setPrearrangeCallback( commit_callback_t cb ) { mPrearrangeCallback = cb; } void setTextEntryCallback( commit_callback_t cb ) { mTextEntryCallback = cb; } + void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; } void setButtonVisible(BOOL visible); @@ -220,6 +222,7 @@ private: BOOL mTextEntryTentative; commit_callback_t mPrearrangeCallback; commit_callback_t mTextEntryCallback; + commit_callback_t mTextChangedCallback; commit_callback_t mSelectionCallback; boost::signals2::connection mTopLostSignalConnection; S32 mLastSelectedIndex; diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 4f87221065..9f1d3a2a7d 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -611,10 +611,9 @@ void LLFavoritesBarCtrl::changed(U32 mask) LLIsType is_type(LLAssetType::AT_LANDMARK); gInventory.collectDescendentsIf(mFavoriteFolderId, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); - S32 sortField = 0; for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) { - (*i)->setSortField(++sortField); + (*i)->getSLURL(); } updateButtons(); } diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 16ea303c77..c50e8c48b5 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -266,26 +266,51 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, // Show last logged in user favorites in "Start at" combo if corresponding option is enabled. if (gSavedSettings.getBOOL("ShowFavoritesOnLogin")) { - addFavoritesToStartLocation(); + addUsersWithFavoritesToUsername(); + getChild("username_combo")->setTextChangedCallback(boost::bind(&LLPanelLogin::addFavoritesToStartLocation, this)); } updateLocationCombo(false); } -void LLPanelLogin::addFavoritesToStartLocation() +void LLPanelLogin::addUsersWithFavoritesToUsername() { + LLComboBox* combo = getChild("username_combo"); + if (!combo) return; std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); LLSD fav_llsd; llifstream file; file.open(filename); if (!file.is_open()) return; + LLSDSerialize::fromXML(fav_llsd, file); + for (LLSD::map_const_iterator iter = fav_llsd.beginMap(); + iter != fav_llsd.endMap(); ++iter) + { + combo->add(iter->first); + } +} + +void LLPanelLogin::addFavoritesToStartLocation() +{ LLComboBox* combo = getChild("start_location_combo"); - combo->addSeparator(); + if (!combo) return; + int num_items = combo->getItemCount(); + for (int i = num_items - 1; i > 2; i--) + { + combo->remove(i); + } + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; LLSDSerialize::fromXML(fav_llsd, file); for (LLSD::map_const_iterator iter = fav_llsd.beginMap(); iter != fav_llsd.endMap(); ++iter) { + if(iter->first != getChild("username_combo")->getSimple()) continue; + combo->addSeparator(); LLSD user_llsd = iter->second; for (LLSD::array_const_iterator iter1 = user_llsd.beginArray(); iter1 != user_llsd.endArray(); ++iter1) @@ -297,7 +322,7 @@ void LLPanelLogin::addFavoritesToStartLocation() combo->add(label, value); } } - + break; } } @@ -461,13 +486,14 @@ void LLPanelLogin::giveFocus() if( sInstance ) { // Grab focus and move cursor to first blank input field - std::string username = sInstance->getChild("username_edit")->getValue().asString(); + std::string username = sInstance->getChild("username_combo")->getValue().asString(); std::string pass = sInstance->getChild("password_edit")->getValue().asString(); BOOL have_username = !username.empty(); BOOL have_pass = !pass.empty(); LLLineEditor* edit = NULL; + LLComboBox* combo = NULL; if (have_username && !have_pass) { // User saved his name but not his password. Move @@ -477,7 +503,7 @@ void LLPanelLogin::giveFocus() else { // User doesn't have a name, so start there. - edit = sInstance->getChild("username_edit"); + combo = sInstance->getChild("username_combo"); } if (edit) @@ -485,6 +511,10 @@ void LLPanelLogin::giveFocus() edit->setFocus(TRUE); edit->selectAll(); } + else if (combo) + { + combo->setFocus(TRUE); + } } #endif } @@ -498,8 +528,8 @@ void LLPanelLogin::showLoginWidgets() // *TODO: Append all the usual login parameters, like first_login=Y etc. std::string splash_screen_url = sInstance->getString("real_url"); web_browser->navigateTo( splash_screen_url, "text/html" ); - LLUICtrl* username_edit = sInstance->getChild("username_edit"); - username_edit->setFocus(TRUE); + LLUICtrl* username_combo = sInstance->getChild("username_combo"); + username_combo->setFocus(TRUE); } // static @@ -543,15 +573,19 @@ void LLPanelLogin::setFields(LLPointer credential, login_id += " "; login_id += lastname; } - sInstance->getChild("username_edit")->setValue(login_id); + sInstance->getChild("username_combo")->setLabel(login_id); } else if((std::string)identifier["type"] == "account") { - sInstance->getChild("username_edit")->setValue((std::string)identifier["account_name"]); + sInstance->getChild("username_combo")->setLabel((std::string)identifier["account_name"]); } else { - sInstance->getChild("username_edit")->setValue(std::string()); + sInstance->getChild("username_combo")->setLabel(std::string()); + } + if (gSavedSettings.getBOOL("ShowFavoritesOnLogin")) + { + sInstance->addFavoritesToStartLocation(); } // if the password exists in the credential, set the password field with // a filler to get some stars @@ -600,7 +634,7 @@ void LLPanelLogin::getFields(LLPointer& credential, authenticator = credential->getAuthenticator(); } - std::string username = sInstance->getChild("username_edit")->getValue().asString(); + std::string username = sInstance->getChild("username_combo")->getValue().asString(); LLStringUtil::trim(username); std::string password = sInstance->getChild("password_edit")->getValue().asString(); @@ -692,15 +726,15 @@ BOOL LLPanelLogin::areCredentialFieldsDirty() } else { - std::string username = sInstance->getChild("username_edit")->getValue().asString(); + std::string username = sInstance->getChild("username_combo")->getValue().asString(); LLStringUtil::trim(username); std::string password = sInstance->getChild("password_edit")->getValue().asString(); - LLLineEditor* ctrl = sInstance->getChild("username_edit"); - if(ctrl && ctrl->isDirty()) + LLComboBox* combo = sInstance->getChild("username_combo"); + if(combo && combo->isDirty()) { return true; } - ctrl = sInstance->getChild("password_edit"); + LLLineEditor* ctrl = sInstance->getChild("password_edit"); if(ctrl && ctrl->isDirty()) { return true; @@ -1007,7 +1041,7 @@ void LLPanelLogin::onClickConnect(void *) return; } updateStartSLURL(); - std::string username = sInstance->getChild("username_edit")->getValue().asString(); + std::string username = sInstance->getChild("username_combo")->getValue().asString(); if(username.empty()) diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 8a8888a053..1ef6539ecc 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -85,8 +85,8 @@ public: private: friend class LLPanelLoginListener; void reshapeBrowser(); - // adds favorites of last logged in user from file to "Start at" combobox. void addFavoritesToStartLocation(); + void addUsersWithFavoritesToUsername(); static void onClickConnect(void*); static void onClickNewAccount(void*); // static bool newAccountAlertCallback(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 941e81d36f..4fa79b1855 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1562,6 +1562,13 @@ void LLFavoritesOrderStorage::saveFavoritesSLURLs() if (user_dir.empty()) return; std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + llifstream in_file; + in_file.open(filename); + LLSD fav_llsd; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, in_file); + } const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); LLInventoryModel::cat_array_t cats; @@ -1579,18 +1586,10 @@ void LLFavoritesOrderStorage::saveFavoritesSLURLs() if (slurl_iter != mSLURLs.end()) { value["slurl"] = slurl_iter->second; + user_llsd[(*it)->getSortField()] = value; } - else - { - llwarns << "Fetching SLURLs for \"Favorites\" is not complete!" << llendl; - return; - } - - user_llsd[(*it)->getSortField()] = value; } - LLSD fav_llsd; - // this level in LLSD is not needed now and is just a stub, but will be needed later when implementing save of multiple users favorites in one file. LLAvatarName av_name; LLAvatarNameCache::get( gAgentID, &av_name ); fav_llsd[av_name.getLegacyName()] = user_llsd; @@ -1680,6 +1679,11 @@ S32 LLViewerInventoryItem::getSortField() const void LLViewerInventoryItem::setSortField(S32 sortField) { LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); + getSLURL(); +} + +void LLViewerInventoryItem::getSLURL() +{ LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID); } diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 1af06a1be8..41542a4e0f 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -62,6 +62,7 @@ public: virtual const std::string& getName() const; virtual S32 getSortField() const; virtual void setSortField(S32 sortField); + virtual void getSLURL(); //Caches SLURL for landmark. //*TODO: Find a better way to do it and remove this method from here. virtual const LLPermissions& getPermissions() const; virtual const bool getIsFullPerm() const; // 'fullperm' in the popular sense: modify-ok & copy-ok & transfer-ok, no special god rules applied virtual const LLUUID& getCreatorUUID() const; diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index b181ca3bba..5ad8d1fd40 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -64,23 +64,28 @@ left="20" width="150"> Username: - +name="username_combo" +width="178"> + + + @@ -127,7 +132,7 @@ top="20" Date: Thu, 23 Dec 2010 18:15:54 +0200 Subject: STORM-34 ADDITIONAL_FIX Made saving favorites in file per-account preference instead of per-machine. - Made changes in code of floater preferences and panel login that were required because of turning the setting per-account. - Added new method to LLFloaterPreference that looks for current user's record in saved favorites file and removes it. --- indra/newview/app_settings/settings.xml | 11 ------ .../newview/app_settings/settings_per_account.xml | 11 ++++++ indra/newview/llfloaterpreference.cpp | 41 ++++++++++++++++++---- indra/newview/llfloaterpreference.h | 5 ++- indra/newview/llpanellogin.cpp | 14 +++----- indra/newview/llviewerinventory.cpp | 2 +- .../default/xui/en/panel_preferences_privacy.xml | 1 + 7 files changed, 55 insertions(+), 30 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 871053782b..402a0e85c4 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12332,16 +12332,5 @@ Value name - ShowFavoritesOnLogin - - Comment - Determines whether favorites of last logged in user will be saved on exit from viewer and shown on login screen - Persist - 1 - Type - Boolean - Value - 0 - diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 705c73cbf7..8efec1cff0 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -160,6 +160,17 @@ Value 0 + ShowFavoritesOnLogin + + Comment + Determines whether favorites of last logged in user will be saved on exit from viewer and shown on login screen + Persist + 1 + Type + Boolean + Value + 0 + diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 6500aefb10..84b7fbcce2 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -105,6 +105,7 @@ #include "llteleporthistorystorage.h" #include "lllogininstance.h" // to check if logged in yet +#include "llsdserialize.h" const F32 MAX_USER_FAR_CLIP = 512.f; const F32 MIN_USER_FAR_CLIP = 64.f; @@ -284,7 +285,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mGotPersonalInfo(false), mOriginalIMViaEmail(false), mLanguageChanged(false), - mDoubleClickActionDirty(false) + mDoubleClickActionDirty(false), + mFavoritesRecordMayExist(false) { //Build Floater is now Called from LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -330,8 +332,6 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); gSavedSettings.getControl("UseDisplayNames")->getCommitSignal()->connect(boost::bind(&handleDisplayNamesOptionChanged, _2)); - - mFavoritesFileMayExist = gSavedSettings.getBOOL("ShowFavoritesOnLogin"); } BOOL LLFloaterPreference::postBuild() @@ -492,12 +492,33 @@ void LLFloaterPreference::apply() mDoubleClickActionDirty = false; } - if (mFavoritesFileMayExist && !gSavedSettings.getBOOL("ShowFavoritesOnLogin")) + if (mFavoritesRecordMayExist && !gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) + { + removeFavoritesRecordOfUser(); + } +} + +void LLFloaterPreference::removeFavoritesRecordOfUser() +{ + mFavoritesRecordMayExist = false; + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + LLSDSerialize::fromXML(fav_llsd, file); + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + if (fav_llsd.has(av_name.getLegacyName())) { - mFavoritesFileMayExist = false; - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - LLFile::remove(filename); + fav_llsd.erase(av_name.getLegacyName()); } + + llofstream out_file; + out_file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, out_file); + } void LLFloaterPreference::cancel() @@ -582,6 +603,11 @@ void LLFloaterPreference::onOpen(const LLSD& key) getChildView("maturity_desired_combobox")->setVisible( false); } + if (LLStartUp::getStartupState() == STATE_STARTED) + { + mFavoritesRecordMayExist = gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin"); + } + // Forget previous language changes. mLanguageChanged = false; @@ -1285,6 +1311,7 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im // getChild("busy_response")->setValue(gSavedSettings.getString("BusyModeResponse2")); + getChildView("favorites_on_login_check")->setEnabled(TRUE); getChildView("log_nearby_chat")->setEnabled(TRUE); getChildView("log_instant_messages")->setEnabled(TRUE); getChildView("show_timestamps_check_im")->setEnabled(TRUE); diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index c95a2472a7..4522d9e6eb 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -152,6 +152,8 @@ public: void buildPopupLists(); static void refreshSkin(void* data); + // Remove record of current user's favorites from file on disk. + void removeFavoritesRecordOfUser(); private: static std::string sSkin; // set true if state of double-click action checkbox or radio-group was changed by user @@ -162,7 +164,8 @@ private: bool mLanguageChanged; bool mOriginalHideOnlineStatus; - bool mFavoritesFileMayExist; + // Record of current user's favorites may be stored in file on disk. + bool mFavoritesRecordMayExist; std::string mDirectoryVisibility; }; diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index c50e8c48b5..347190da51 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -263,12 +263,9 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); - // Show last logged in user favorites in "Start at" combo if corresponding option is enabled. - if (gSavedSettings.getBOOL("ShowFavoritesOnLogin")) - { - addUsersWithFavoritesToUsername(); - getChild("username_combo")->setTextChangedCallback(boost::bind(&LLPanelLogin::addFavoritesToStartLocation, this)); - } + // Show last logged in user favorites in "Start at" combo. + addUsersWithFavoritesToUsername(); + getChild("username_combo")->setTextChangedCallback(boost::bind(&LLPanelLogin::addFavoritesToStartLocation, this)); updateLocationCombo(false); @@ -583,10 +580,7 @@ void LLPanelLogin::setFields(LLPointer credential, { sInstance->getChild("username_combo")->setLabel(std::string()); } - if (gSavedSettings.getBOOL("ShowFavoritesOnLogin")) - { - sInstance->addFavoritesToStartLocation(); - } + sInstance->addFavoritesToStartLocation(); // if the password exists in the credential, set the password field with // a filler to get some stars LLSD authenticator = credential->getAuthenticator(); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 4fa79b1855..9c7ef7922b 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1527,7 +1527,7 @@ void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) void LLFavoritesOrderStorage::destroyClass() { LLFavoritesOrderStorage::instance().cleanup(); - if (gSavedSettings.getBOOL("ShowFavoritesOnLogin")) + if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) { LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); } diff --git a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml index 85d3859f63..9d012550fc 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml @@ -71,6 +71,7 @@ width="350" /> Date: Thu, 6 Jan 2011 11:06:20 -0500 Subject: STORM-826 (workaround): correct mixed and dos-style line endings --- indra/cmake/GetPrerequisites_2_8.cmake | 1572 ++++++++++---------- indra/cmake/LLAddBuildTest.cmake | 552 +++---- indra/newview/llfloaterwebcontent.cpp | 804 +++++----- indra/newview/llfloaterwebcontent.h | 162 +- indra/newview/llimview.cpp | 26 +- indra/newview/llimview.h | 2 +- indra/newview/lllogchat.cpp | 18 +- indra/newview/tests/llremoteparcelrequest_test.cpp | 268 ++-- .../updater/tests/llupdaterservice_test.cpp | 400 ++--- 9 files changed, 1902 insertions(+), 1902 deletions(-) (limited to 'indra') diff --git a/indra/cmake/GetPrerequisites_2_8.cmake b/indra/cmake/GetPrerequisites_2_8.cmake index 5a24842c89..05ec1539ba 100644 --- a/indra/cmake/GetPrerequisites_2_8.cmake +++ b/indra/cmake/GetPrerequisites_2_8.cmake @@ -1,786 +1,786 @@ -# GetPrerequisites.cmake -# -# This script provides functions to list the .dll, .dylib or .so files that an -# executable or shared library file depends on. (Its prerequisites.) -# -# It uses various tools to obtain the list of required shared library files: -# dumpbin (Windows) -# ldd (Linux/Unix) -# otool (Mac OSX) -# -# The following functions are provided by this script: -# gp_append_unique -# is_file_executable -# gp_item_default_embedded_path -# (projects can override with gp_item_default_embedded_path_override) -# gp_resolve_item -# (projects can override with gp_resolve_item_override) -# gp_resolved_file_type -# gp_file_type -# get_prerequisites -# list_prerequisites -# list_prerequisites_by_glob -# -# Requires CMake 2.6 or greater because it uses function, break, return and -# PARENT_SCOPE. - -#============================================================================= -# Copyright 2008-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distributed this file outside of CMake, substitute the full -# License text for the above reference.) - -# gp_append_unique list_var value -# -# Append value to the list variable ${list_var} only if the value is not -# already in the list. -# -function(gp_append_unique list_var value) - set(contains 0) - - foreach(item ${${list_var}}) - if("${item}" STREQUAL "${value}") - set(contains 1) - break() - endif("${item}" STREQUAL "${value}") - endforeach(item) - - if(NOT contains) - set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE) - endif(NOT contains) -endfunction(gp_append_unique) - - -# is_file_executable file result_var -# -# Return 1 in ${result_var} if ${file} is a binary executable. -# -# Return 0 in ${result_var} otherwise. -# -function(is_file_executable file result_var) - # - # A file is not executable until proven otherwise: - # - set(${result_var} 0 PARENT_SCOPE) - - get_filename_component(file_full "${file}" ABSOLUTE) - string(TOLOWER "${file_full}" file_full_lower) - - # If file name ends in .exe on Windows, *assume* executable: - # - if(WIN32) - if("${file_full_lower}" MATCHES "\\.exe$") - set(${result_var} 1 PARENT_SCOPE) - return() - endif("${file_full_lower}" MATCHES "\\.exe$") - - # A clause could be added here that uses output or return value of dumpbin - # to determine ${result_var}. In 99%+? practical cases, the exe name - # match will be sufficient... - # - endif(WIN32) - - # Use the information returned from the Unix shell command "file" to - # determine if ${file_full} should be considered an executable file... - # - # If the file command's output contains "executable" and does *not* contain - # "text" then it is likely an executable suitable for prerequisite analysis - # via the get_prerequisites macro. - # - if(UNIX) - if(NOT file_cmd) - find_program(file_cmd "file") - endif(NOT file_cmd) - - if(file_cmd) - execute_process(COMMAND "${file_cmd}" "${file_full}" - OUTPUT_VARIABLE file_ov - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - # Replace the name of the file in the output with a placeholder token - # (the string " _file_full_ ") so that just in case the path name of - # the file contains the word "text" or "executable" we are not fooled - # into thinking "the wrong thing" because the file name matches the - # other 'file' command output we are looking for... - # - string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}") - string(TOLOWER "${file_ov}" file_ov) - - #message(STATUS "file_ov='${file_ov}'") - if("${file_ov}" MATCHES "executable") - #message(STATUS "executable!") - if("${file_ov}" MATCHES "text") - #message(STATUS "but text, so *not* a binary executable!") - else("${file_ov}" MATCHES "text") - set(${result_var} 1 PARENT_SCOPE) - return() - endif("${file_ov}" MATCHES "text") - endif("${file_ov}" MATCHES "executable") - else(file_cmd) - message(STATUS "warning: No 'file' command, skipping execute_process...") - endif(file_cmd) - endif(UNIX) -endfunction(is_file_executable) - - -# gp_item_default_embedded_path item default_embedded_path_var -# -# Return the path that others should refer to the item by when the item -# is embedded inside a bundle. -# -# Override on a per-project basis by providing a project-specific -# gp_item_default_embedded_path_override function. -# -function(gp_item_default_embedded_path item default_embedded_path_var) - - # On Windows and Linux, "embed" prerequisites in the same directory - # as the executable by default: - # - set(path "@executable_path") - set(overridden 0) - - # On the Mac, relative to the executable depending on the type - # of the thing we are embedding: - # - if(APPLE) - # - # The assumption here is that all executables in the bundle will be - # in same-level-directories inside the bundle. The parent directory - # of an executable inside the bundle should be MacOS or a sibling of - # MacOS and all embedded paths returned from here will begin with - # "@executable_path/../" and will work from all executables in all - # such same-level-directories inside the bundle. - # - - # By default, embed things right next to the main bundle executable: - # - set(path "@executable_path/../../Contents/MacOS") - - # Embed .dylibs right next to the main bundle executable: - # - if(item MATCHES "\\.dylib$") - set(path "@executable_path/../MacOS") - set(overridden 1) - endif(item MATCHES "\\.dylib$") - - # Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS): - # - if(NOT overridden) - if(item MATCHES "[^/]+\\.framework/") - set(path "@executable_path/../Frameworks") - set(overridden 1) - endif(item MATCHES "[^/]+\\.framework/") - endif(NOT overridden) - endif() - - # Provide a hook so that projects can override the default embedded location - # of any given library by whatever logic they choose: - # - if(COMMAND gp_item_default_embedded_path_override) - gp_item_default_embedded_path_override("${item}" path) - endif(COMMAND gp_item_default_embedded_path_override) - - set(${default_embedded_path_var} "${path}" PARENT_SCOPE) -endfunction(gp_item_default_embedded_path) - - -# gp_resolve_item context item exepath dirs resolved_item_var -# -# Resolve an item into an existing full path file. -# -# Override on a per-project basis by providing a project-specific -# gp_resolve_item_override function. -# -function(gp_resolve_item context item exepath dirs resolved_item_var) - set(resolved 0) - set(resolved_item "${item}") - - # Is it already resolved? - # - if(EXISTS "${resolved_item}") - set(resolved 1) - endif(EXISTS "${resolved_item}") - - if(NOT resolved) - if(item MATCHES "@executable_path") - # - # @executable_path references are assumed relative to exepath - # - string(REPLACE "@executable_path" "${exepath}" ri "${item}") - get_filename_component(ri "${ri}" ABSOLUTE) - - if(EXISTS "${ri}") - #message(STATUS "info: embedded item exists (${ri})") - set(resolved 1) - set(resolved_item "${ri}") - else(EXISTS "${ri}") - message(STATUS "warning: embedded item does not exist '${ri}'") - endif(EXISTS "${ri}") - endif(item MATCHES "@executable_path") - endif(NOT resolved) - - if(NOT resolved) - if(item MATCHES "@loader_path") - # - # @loader_path references are assumed relative to the - # PATH of the given "context" (presumably another library) - # - get_filename_component(contextpath "${context}" PATH) - string(REPLACE "@loader_path" "${contextpath}" ri "${item}") - get_filename_component(ri "${ri}" ABSOLUTE) - - if(EXISTS "${ri}") - #message(STATUS "info: embedded item exists (${ri})") - set(resolved 1) - set(resolved_item "${ri}") - else(EXISTS "${ri}") - message(STATUS "warning: embedded item does not exist '${ri}'") - endif(EXISTS "${ri}") - endif(item MATCHES "@loader_path") - endif(NOT resolved) - - if(NOT resolved) - set(ri "ri-NOTFOUND") - find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH) - find_file(ri "${item}" ${exepath} ${dirs} /usr/lib) - if(ri) - #message(STATUS "info: 'find_file' in exepath/dirs (${ri})") - set(resolved 1) - set(resolved_item "${ri}") - set(ri "ri-NOTFOUND") - endif(ri) - endif(NOT resolved) - - if(NOT resolved) - if(item MATCHES "[^/]+\\.framework/") - set(fw "fw-NOTFOUND") - find_file(fw "${item}" - "~/Library/Frameworks" - "/Library/Frameworks" - "/System/Library/Frameworks" - ) - if(fw) - #message(STATUS "info: 'find_file' found framework (${fw})") - set(resolved 1) - set(resolved_item "${fw}") - set(fw "fw-NOTFOUND") - endif(fw) - endif(item MATCHES "[^/]+\\.framework/") - endif(NOT resolved) - - # Using find_program on Windows will find dll files that are in the PATH. - # (Converting simple file names into full path names if found.) - # - if(WIN32) - if(NOT resolved) - set(ri "ri-NOTFOUND") - find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH) - find_program(ri "${item}" PATHS "${exepath};${dirs}") - if(ri) - #message(STATUS "info: 'find_program' in exepath/dirs (${ri})") - set(resolved 1) - set(resolved_item "${ri}") - set(ri "ri-NOTFOUND") - endif(ri) - endif(NOT resolved) - endif(WIN32) - - # Provide a hook so that projects can override item resolution - # by whatever logic they choose: - # - if(COMMAND gp_resolve_item_override) - gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved) - endif(COMMAND gp_resolve_item_override) - - if(NOT resolved) - message(STATUS " -warning: cannot resolve item '${item}' - - possible problems: - need more directories? - need to use InstallRequiredSystemLibraries? - run in install tree instead of build tree? -") -# message(STATUS " -#****************************************************************************** -#warning: cannot resolve item '${item}' -# -# possible problems: -# need more directories? -# need to use InstallRequiredSystemLibraries? -# run in install tree instead of build tree? -# -# context='${context}' -# item='${item}' -# exepath='${exepath}' -# dirs='${dirs}' -# resolved_item_var='${resolved_item_var}' -#****************************************************************************** -#") - endif(NOT resolved) - - set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE) -endfunction(gp_resolve_item) - - -# gp_resolved_file_type original_file file exepath dirs type_var -# -# Return the type of ${file} with respect to ${original_file}. String -# describing type of prerequisite is returned in variable named ${type_var}. -# -# Use ${exepath} and ${dirs} if necessary to resolve non-absolute ${file} -# values -- but only for non-embedded items. -# -# Possible types are: -# system -# local -# embedded -# other -# -function(gp_resolved_file_type original_file file exepath dirs type_var) - #message(STATUS "**") - - if(NOT IS_ABSOLUTE "${original_file}") - message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file") - endif() - - set(is_embedded 0) - set(is_local 0) - set(is_system 0) - - set(resolved_file "${file}") - - if("${file}" MATCHES "^@(executable|loader)_path") - set(is_embedded 1) - endif() - - if(NOT is_embedded) - if(NOT IS_ABSOLUTE "${file}") - gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file) - endif() - - string(TOLOWER "${original_file}" original_lower) - string(TOLOWER "${resolved_file}" lower) - - if(UNIX) - if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/)") - set(is_system 1) - endif() - endif() - - if(APPLE) - if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)") - set(is_system 1) - endif() - endif() - - if(WIN32) - string(TOLOWER "$ENV{SystemRoot}" sysroot) - string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}") - - string(TOLOWER "$ENV{windir}" windir) - string(REGEX REPLACE "\\\\" "/" windir "${windir}") - - if(lower MATCHES "^(${sysroot}/system|${windir}/system|${sysroot}/syswow|${windir}/syswow|(.*/)*msvc[^/]+dll)") - set(is_system 1) - endif() - endif() - - if(NOT is_system) - get_filename_component(original_path "${original_lower}" PATH) - get_filename_component(path "${lower}" PATH) - if("${original_path}" STREQUAL "${path}") - set(is_local 1) - endif() - endif() - endif() - - # Return type string based on computed booleans: - # - set(type "other") - - if(is_system) - set(type "system") - elseif(is_embedded) - set(type "embedded") - elseif(is_local) - set(type "local") - endif() - - #message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'") - #message(STATUS " type: '${type}'") - - if(NOT is_embedded) - if(NOT IS_ABSOLUTE "${resolved_file}") - if(lower MATCHES "^msvc[^/]+dll" AND is_system) - message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'") - else() - message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect") - endif() - endif() - endif() - - set(${type_var} "${type}" PARENT_SCOPE) - - #message(STATUS "**") -endfunction() - - -# gp_file_type original_file file type_var -# -# Return the type of ${file} with respect to ${original_file}. String -# describing type of prerequisite is returned in variable named ${type_var}. -# -# Possible types are: -# system -# local -# embedded -# other -# -function(gp_file_type original_file file type_var) - if(NOT IS_ABSOLUTE "${original_file}") - message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file") - endif() - - get_filename_component(exepath "${original_file}" PATH) - - set(type "") - gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type) - - set(${type_var} "${type}" PARENT_SCOPE) -endfunction(gp_file_type) - - -# get_prerequisites target prerequisites_var exclude_system recurse dirs -# -# Get the list of shared library files required by ${target}. The list in -# the variable named ${prerequisites_var} should be empty on first entry to -# this function. On exit, ${prerequisites_var} will contain the list of -# required shared library files. -# -# target is the full path to an executable file -# -# prerequisites_var is the name of a CMake variable to contain the results -# -# exclude_system is 0 or 1: 0 to include "system" prerequisites , 1 to -# exclude them -# -# recurse is 0 or 1: 0 for direct prerequisites only, 1 for all prerequisites -# recursively -# -# exepath is the path to the top level executable used for @executable_path -# replacment on the Mac -# -# dirs is a list of paths where libraries might be found: these paths are -# searched first when a target without any path info is given. Then standard -# system locations are also searched: PATH, Framework locations, /usr/lib... -# -function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs) - set(verbose 0) - set(eol_char "E") - - if(NOT IS_ABSOLUTE "${target}") - message("warning: target '${target}' is not absolute...") - endif(NOT IS_ABSOLUTE "${target}") - - if(NOT EXISTS "${target}") - message("warning: target '${target}' does not exist...") - endif(NOT EXISTS "${target}") - - # - # - # Try to choose the right tool by default. Caller can set gp_tool prior to - # calling this function to force using a different tool. - # - if("${gp_tool}" STREQUAL "") - set(gp_tool "ldd") - if(APPLE) - set(gp_tool "otool") - endif(APPLE) - if(WIN32) - set(gp_tool "dumpbin") - endif(WIN32) - endif("${gp_tool}" STREQUAL "") - - set(gp_tool_known 0) - - if("${gp_tool}" STREQUAL "ldd") - set(gp_cmd_args "") - set(gp_regex "^[\t ]*[^\t ]+ => ([^\t ]+).*${eol_char}$") - set(gp_regex_cmp_count 1) - set(gp_tool_known 1) - endif("${gp_tool}" STREQUAL "ldd") - - if("${gp_tool}" STREQUAL "otool") - set(gp_cmd_args "-L") - set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$") - set(gp_regex_cmp_count 3) - set(gp_tool_known 1) - endif("${gp_tool}" STREQUAL "otool") - - if("${gp_tool}" STREQUAL "dumpbin") - set(gp_cmd_args "/dependents") - set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$") - set(gp_regex_cmp_count 1) - set(gp_tool_known 1) - set(ENV{VS_UNICODE_OUTPUT} "") # Block extra output from inside VS IDE. - endif("${gp_tool}" STREQUAL "dumpbin") - - if(NOT gp_tool_known) - message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...") - message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'") - message(STATUS "Valid gp_tool values are dumpbin, ldd and otool.") - return() - endif(NOT gp_tool_known) - - set(gp_cmd_paths ${gp_cmd_paths} - "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin" - "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin" - "C:/Program Files/Microsoft Visual Studio 8/VC/BIN" - "C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN" - "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN" - "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN" - "/usr/local/bin" - "/usr/bin" - ) - - find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths}) - - if(NOT gp_cmd) - message(STATUS "warning: could not find '${gp_tool}' - cannot analyze prerequisites...") - return() - endif(NOT gp_cmd) - - if("${gp_tool}" STREQUAL "dumpbin") - # When running dumpbin, it also needs the "Common7/IDE" directory in the - # PATH. It will already be in the PATH if being run from a Visual Studio - # command prompt. Add it to the PATH here in case we are running from a - # different command prompt. - # - get_filename_component(gp_cmd_dir "${gp_cmd}" PATH) - get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE) - if(EXISTS "${gp_cmd_dlls_dir}") - # only add to the path if it is not already in the path - if(NOT "$ENV{PATH}" MATCHES "${gp_cmd_dlls_dir}") - set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}") - endif(NOT "$ENV{PATH}" MATCHES "${gp_cmd_dlls_dir}") - endif(EXISTS "${gp_cmd_dlls_dir}") - endif("${gp_tool}" STREQUAL "dumpbin") - # - # - - if("${gp_tool}" STREQUAL "ldd") - set(old_ld_env "$ENV{LD_LIBRARY_PATH}") - foreach(dir ${exepath} ${dirs}) - set(ENV{LD_LIBRARY_PATH} "${dir}:$ENV{LD_LIBRARY_PATH}") - endforeach(dir) - endif("${gp_tool}" STREQUAL "ldd") - - - # Track new prerequisites at each new level of recursion. Start with an - # empty list at each level: - # - set(unseen_prereqs) - - # Run gp_cmd on the target: - # - execute_process( - COMMAND ${gp_cmd} ${gp_cmd_args} ${target} - OUTPUT_VARIABLE gp_cmd_ov - ) - - if("${gp_tool}" STREQUAL "ldd") - set(ENV{LD_LIBRARY_PATH} "${old_ld_env}") - endif("${gp_tool}" STREQUAL "ldd") - - if(verbose) - message(STATUS "") - message(STATUS "gp_cmd_ov='${gp_cmd_ov}'") - message(STATUS "") - endif(verbose) - - get_filename_component(target_dir "${target}" PATH) - - # Convert to a list of lines: - # - string(REGEX REPLACE ";" "\\\\;" candidates "${gp_cmd_ov}") - string(REGEX REPLACE "\n" "${eol_char};" candidates "${candidates}") - - # Analyze each line for file names that match the regular expression: - # - foreach(candidate ${candidates}) - if("${candidate}" MATCHES "${gp_regex}") - # Extract information from each candidate: - string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}") - - if(gp_regex_cmp_count GREATER 1) - string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}") - string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}") - string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}") - string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}") - endif(gp_regex_cmp_count GREATER 1) - - if(gp_regex_cmp_count GREATER 2) - string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}") - string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}") - string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}") - string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}") - endif(gp_regex_cmp_count GREATER 2) - - # Use the raw_item as the list entries returned by this function. Use the - # gp_resolve_item function to resolve it to an actual full path file if - # necessary. - # - set(item "${raw_item}") - - # Add each item unless it is excluded: - # - set(add_item 1) - - if(${exclude_system}) - set(type "") - gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type) - if("${type}" STREQUAL "system") - set(add_item 0) - endif("${type}" STREQUAL "system") - endif(${exclude_system}) - - if(add_item) - list(LENGTH ${prerequisites_var} list_length_before_append) - gp_append_unique(${prerequisites_var} "${item}") - list(LENGTH ${prerequisites_var} list_length_after_append) - - if(${recurse}) - # If item was really added, this is the first time we have seen it. - # Add it to unseen_prereqs so that we can recursively add *its* - # prerequisites... - # - # But first: resolve its name to an absolute full path name such - # that the analysis tools can simply accept it as input. - # - if(NOT list_length_before_append EQUAL list_length_after_append) - gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item) - set(unseen_prereqs ${unseen_prereqs} "${resolved_item}") - endif(NOT list_length_before_append EQUAL list_length_after_append) - endif(${recurse}) - endif(add_item) - else("${candidate}" MATCHES "${gp_regex}") - if(verbose) - message(STATUS "ignoring non-matching line: '${candidate}'") - endif(verbose) - endif("${candidate}" MATCHES "${gp_regex}") - endforeach(candidate) - - list(LENGTH ${prerequisites_var} prerequisites_var_length) - if(prerequisites_var_length GREATER 0) - list(SORT ${prerequisites_var}) - endif(prerequisites_var_length GREATER 0) - if(${recurse}) - set(more_inputs ${unseen_prereqs}) - foreach(input ${more_inputs}) - get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}") - endforeach(input) - endif(${recurse}) - - set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE) -endfunction(get_prerequisites) - - -# list_prerequisites target all exclude_system verbose -# -# ARGV0 (target) is the full path to an executable file -# -# optional ARGV1 (all) is 0 or 1: 0 for direct prerequisites only, -# 1 for all prerequisites recursively -# -# optional ARGV2 (exclude_system) is 0 or 1: 0 to include "system" -# prerequisites , 1 to exclude them -# -# optional ARGV3 (verbose) is 0 or 1: 0 to print only full path -# names of prerequisites, 1 to print extra information -# -function(list_prerequisites target) - if("${ARGV1}" STREQUAL "") - set(all 1) - else("${ARGV1}" STREQUAL "") - set(all "${ARGV1}") - endif("${ARGV1}" STREQUAL "") - - if("${ARGV2}" STREQUAL "") - set(exclude_system 0) - else("${ARGV2}" STREQUAL "") - set(exclude_system "${ARGV2}") - endif("${ARGV2}" STREQUAL "") - - if("${ARGV3}" STREQUAL "") - set(verbose 0) - else("${ARGV3}" STREQUAL "") - set(verbose "${ARGV3}") - endif("${ARGV3}" STREQUAL "") - - set(count 0) - set(count_str "") - set(print_count "${verbose}") - set(print_prerequisite_type "${verbose}") - set(print_target "${verbose}") - set(type_str "") - - get_filename_component(exepath "${target}" PATH) - - set(prereqs "") - get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "") - - if(print_target) - message(STATUS "File '${target}' depends on:") - endif(print_target) - - foreach(d ${prereqs}) - math(EXPR count "${count} + 1") - - if(print_count) - set(count_str "${count}. ") - endif(print_count) - - if(print_prerequisite_type) - gp_file_type("${target}" "${d}" type) - set(type_str " (${type})") - endif(print_prerequisite_type) - - message(STATUS "${count_str}${d}${type_str}") - endforeach(d) -endfunction(list_prerequisites) - - -# list_prerequisites_by_glob glob_arg glob_exp -# -# glob_arg is GLOB or GLOB_RECURSE -# -# glob_exp is a globbing expression used with "file(GLOB" to retrieve a list -# of matching files. If a matching file is executable, its prerequisites are -# listed. -# -# Any additional (optional) arguments provided are passed along as the -# optional arguments to the list_prerequisites calls. -# -function(list_prerequisites_by_glob glob_arg glob_exp) - message(STATUS "=============================================================================") - message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'") - message(STATUS "") - file(${glob_arg} file_list ${glob_exp}) - foreach(f ${file_list}) - is_file_executable("${f}" is_f_executable) - if(is_f_executable) - message(STATUS "=============================================================================") - list_prerequisites("${f}" ${ARGN}) - message(STATUS "") - endif(is_f_executable) - endforeach(f) -endfunction(list_prerequisites_by_glob) +# GetPrerequisites.cmake +# +# This script provides functions to list the .dll, .dylib or .so files that an +# executable or shared library file depends on. (Its prerequisites.) +# +# It uses various tools to obtain the list of required shared library files: +# dumpbin (Windows) +# ldd (Linux/Unix) +# otool (Mac OSX) +# +# The following functions are provided by this script: +# gp_append_unique +# is_file_executable +# gp_item_default_embedded_path +# (projects can override with gp_item_default_embedded_path_override) +# gp_resolve_item +# (projects can override with gp_resolve_item_override) +# gp_resolved_file_type +# gp_file_type +# get_prerequisites +# list_prerequisites +# list_prerequisites_by_glob +# +# Requires CMake 2.6 or greater because it uses function, break, return and +# PARENT_SCOPE. + +#============================================================================= +# Copyright 2008-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +# gp_append_unique list_var value +# +# Append value to the list variable ${list_var} only if the value is not +# already in the list. +# +function(gp_append_unique list_var value) + set(contains 0) + + foreach(item ${${list_var}}) + if("${item}" STREQUAL "${value}") + set(contains 1) + break() + endif("${item}" STREQUAL "${value}") + endforeach(item) + + if(NOT contains) + set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE) + endif(NOT contains) +endfunction(gp_append_unique) + + +# is_file_executable file result_var +# +# Return 1 in ${result_var} if ${file} is a binary executable. +# +# Return 0 in ${result_var} otherwise. +# +function(is_file_executable file result_var) + # + # A file is not executable until proven otherwise: + # + set(${result_var} 0 PARENT_SCOPE) + + get_filename_component(file_full "${file}" ABSOLUTE) + string(TOLOWER "${file_full}" file_full_lower) + + # If file name ends in .exe on Windows, *assume* executable: + # + if(WIN32) + if("${file_full_lower}" MATCHES "\\.exe$") + set(${result_var} 1 PARENT_SCOPE) + return() + endif("${file_full_lower}" MATCHES "\\.exe$") + + # A clause could be added here that uses output or return value of dumpbin + # to determine ${result_var}. In 99%+? practical cases, the exe name + # match will be sufficient... + # + endif(WIN32) + + # Use the information returned from the Unix shell command "file" to + # determine if ${file_full} should be considered an executable file... + # + # If the file command's output contains "executable" and does *not* contain + # "text" then it is likely an executable suitable for prerequisite analysis + # via the get_prerequisites macro. + # + if(UNIX) + if(NOT file_cmd) + find_program(file_cmd "file") + endif(NOT file_cmd) + + if(file_cmd) + execute_process(COMMAND "${file_cmd}" "${file_full}" + OUTPUT_VARIABLE file_ov + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # Replace the name of the file in the output with a placeholder token + # (the string " _file_full_ ") so that just in case the path name of + # the file contains the word "text" or "executable" we are not fooled + # into thinking "the wrong thing" because the file name matches the + # other 'file' command output we are looking for... + # + string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}") + string(TOLOWER "${file_ov}" file_ov) + + #message(STATUS "file_ov='${file_ov}'") + if("${file_ov}" MATCHES "executable") + #message(STATUS "executable!") + if("${file_ov}" MATCHES "text") + #message(STATUS "but text, so *not* a binary executable!") + else("${file_ov}" MATCHES "text") + set(${result_var} 1 PARENT_SCOPE) + return() + endif("${file_ov}" MATCHES "text") + endif("${file_ov}" MATCHES "executable") + else(file_cmd) + message(STATUS "warning: No 'file' command, skipping execute_process...") + endif(file_cmd) + endif(UNIX) +endfunction(is_file_executable) + + +# gp_item_default_embedded_path item default_embedded_path_var +# +# Return the path that others should refer to the item by when the item +# is embedded inside a bundle. +# +# Override on a per-project basis by providing a project-specific +# gp_item_default_embedded_path_override function. +# +function(gp_item_default_embedded_path item default_embedded_path_var) + + # On Windows and Linux, "embed" prerequisites in the same directory + # as the executable by default: + # + set(path "@executable_path") + set(overridden 0) + + # On the Mac, relative to the executable depending on the type + # of the thing we are embedding: + # + if(APPLE) + # + # The assumption here is that all executables in the bundle will be + # in same-level-directories inside the bundle. The parent directory + # of an executable inside the bundle should be MacOS or a sibling of + # MacOS and all embedded paths returned from here will begin with + # "@executable_path/../" and will work from all executables in all + # such same-level-directories inside the bundle. + # + + # By default, embed things right next to the main bundle executable: + # + set(path "@executable_path/../../Contents/MacOS") + + # Embed .dylibs right next to the main bundle executable: + # + if(item MATCHES "\\.dylib$") + set(path "@executable_path/../MacOS") + set(overridden 1) + endif(item MATCHES "\\.dylib$") + + # Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS): + # + if(NOT overridden) + if(item MATCHES "[^/]+\\.framework/") + set(path "@executable_path/../Frameworks") + set(overridden 1) + endif(item MATCHES "[^/]+\\.framework/") + endif(NOT overridden) + endif() + + # Provide a hook so that projects can override the default embedded location + # of any given library by whatever logic they choose: + # + if(COMMAND gp_item_default_embedded_path_override) + gp_item_default_embedded_path_override("${item}" path) + endif(COMMAND gp_item_default_embedded_path_override) + + set(${default_embedded_path_var} "${path}" PARENT_SCOPE) +endfunction(gp_item_default_embedded_path) + + +# gp_resolve_item context item exepath dirs resolved_item_var +# +# Resolve an item into an existing full path file. +# +# Override on a per-project basis by providing a project-specific +# gp_resolve_item_override function. +# +function(gp_resolve_item context item exepath dirs resolved_item_var) + set(resolved 0) + set(resolved_item "${item}") + + # Is it already resolved? + # + if(EXISTS "${resolved_item}") + set(resolved 1) + endif(EXISTS "${resolved_item}") + + if(NOT resolved) + if(item MATCHES "@executable_path") + # + # @executable_path references are assumed relative to exepath + # + string(REPLACE "@executable_path" "${exepath}" ri "${item}") + get_filename_component(ri "${ri}" ABSOLUTE) + + if(EXISTS "${ri}") + #message(STATUS "info: embedded item exists (${ri})") + set(resolved 1) + set(resolved_item "${ri}") + else(EXISTS "${ri}") + message(STATUS "warning: embedded item does not exist '${ri}'") + endif(EXISTS "${ri}") + endif(item MATCHES "@executable_path") + endif(NOT resolved) + + if(NOT resolved) + if(item MATCHES "@loader_path") + # + # @loader_path references are assumed relative to the + # PATH of the given "context" (presumably another library) + # + get_filename_component(contextpath "${context}" PATH) + string(REPLACE "@loader_path" "${contextpath}" ri "${item}") + get_filename_component(ri "${ri}" ABSOLUTE) + + if(EXISTS "${ri}") + #message(STATUS "info: embedded item exists (${ri})") + set(resolved 1) + set(resolved_item "${ri}") + else(EXISTS "${ri}") + message(STATUS "warning: embedded item does not exist '${ri}'") + endif(EXISTS "${ri}") + endif(item MATCHES "@loader_path") + endif(NOT resolved) + + if(NOT resolved) + set(ri "ri-NOTFOUND") + find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH) + find_file(ri "${item}" ${exepath} ${dirs} /usr/lib) + if(ri) + #message(STATUS "info: 'find_file' in exepath/dirs (${ri})") + set(resolved 1) + set(resolved_item "${ri}") + set(ri "ri-NOTFOUND") + endif(ri) + endif(NOT resolved) + + if(NOT resolved) + if(item MATCHES "[^/]+\\.framework/") + set(fw "fw-NOTFOUND") + find_file(fw "${item}" + "~/Library/Frameworks" + "/Library/Frameworks" + "/System/Library/Frameworks" + ) + if(fw) + #message(STATUS "info: 'find_file' found framework (${fw})") + set(resolved 1) + set(resolved_item "${fw}") + set(fw "fw-NOTFOUND") + endif(fw) + endif(item MATCHES "[^/]+\\.framework/") + endif(NOT resolved) + + # Using find_program on Windows will find dll files that are in the PATH. + # (Converting simple file names into full path names if found.) + # + if(WIN32) + if(NOT resolved) + set(ri "ri-NOTFOUND") + find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH) + find_program(ri "${item}" PATHS "${exepath};${dirs}") + if(ri) + #message(STATUS "info: 'find_program' in exepath/dirs (${ri})") + set(resolved 1) + set(resolved_item "${ri}") + set(ri "ri-NOTFOUND") + endif(ri) + endif(NOT resolved) + endif(WIN32) + + # Provide a hook so that projects can override item resolution + # by whatever logic they choose: + # + if(COMMAND gp_resolve_item_override) + gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved) + endif(COMMAND gp_resolve_item_override) + + if(NOT resolved) + message(STATUS " +warning: cannot resolve item '${item}' + + possible problems: + need more directories? + need to use InstallRequiredSystemLibraries? + run in install tree instead of build tree? +") +# message(STATUS " +#****************************************************************************** +#warning: cannot resolve item '${item}' +# +# possible problems: +# need more directories? +# need to use InstallRequiredSystemLibraries? +# run in install tree instead of build tree? +# +# context='${context}' +# item='${item}' +# exepath='${exepath}' +# dirs='${dirs}' +# resolved_item_var='${resolved_item_var}' +#****************************************************************************** +#") + endif(NOT resolved) + + set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE) +endfunction(gp_resolve_item) + + +# gp_resolved_file_type original_file file exepath dirs type_var +# +# Return the type of ${file} with respect to ${original_file}. String +# describing type of prerequisite is returned in variable named ${type_var}. +# +# Use ${exepath} and ${dirs} if necessary to resolve non-absolute ${file} +# values -- but only for non-embedded items. +# +# Possible types are: +# system +# local +# embedded +# other +# +function(gp_resolved_file_type original_file file exepath dirs type_var) + #message(STATUS "**") + + if(NOT IS_ABSOLUTE "${original_file}") + message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file") + endif() + + set(is_embedded 0) + set(is_local 0) + set(is_system 0) + + set(resolved_file "${file}") + + if("${file}" MATCHES "^@(executable|loader)_path") + set(is_embedded 1) + endif() + + if(NOT is_embedded) + if(NOT IS_ABSOLUTE "${file}") + gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file) + endif() + + string(TOLOWER "${original_file}" original_lower) + string(TOLOWER "${resolved_file}" lower) + + if(UNIX) + if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/)") + set(is_system 1) + endif() + endif() + + if(APPLE) + if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)") + set(is_system 1) + endif() + endif() + + if(WIN32) + string(TOLOWER "$ENV{SystemRoot}" sysroot) + string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}") + + string(TOLOWER "$ENV{windir}" windir) + string(REGEX REPLACE "\\\\" "/" windir "${windir}") + + if(lower MATCHES "^(${sysroot}/system|${windir}/system|${sysroot}/syswow|${windir}/syswow|(.*/)*msvc[^/]+dll)") + set(is_system 1) + endif() + endif() + + if(NOT is_system) + get_filename_component(original_path "${original_lower}" PATH) + get_filename_component(path "${lower}" PATH) + if("${original_path}" STREQUAL "${path}") + set(is_local 1) + endif() + endif() + endif() + + # Return type string based on computed booleans: + # + set(type "other") + + if(is_system) + set(type "system") + elseif(is_embedded) + set(type "embedded") + elseif(is_local) + set(type "local") + endif() + + #message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'") + #message(STATUS " type: '${type}'") + + if(NOT is_embedded) + if(NOT IS_ABSOLUTE "${resolved_file}") + if(lower MATCHES "^msvc[^/]+dll" AND is_system) + message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'") + else() + message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect") + endif() + endif() + endif() + + set(${type_var} "${type}" PARENT_SCOPE) + + #message(STATUS "**") +endfunction() + + +# gp_file_type original_file file type_var +# +# Return the type of ${file} with respect to ${original_file}. String +# describing type of prerequisite is returned in variable named ${type_var}. +# +# Possible types are: +# system +# local +# embedded +# other +# +function(gp_file_type original_file file type_var) + if(NOT IS_ABSOLUTE "${original_file}") + message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file") + endif() + + get_filename_component(exepath "${original_file}" PATH) + + set(type "") + gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type) + + set(${type_var} "${type}" PARENT_SCOPE) +endfunction(gp_file_type) + + +# get_prerequisites target prerequisites_var exclude_system recurse dirs +# +# Get the list of shared library files required by ${target}. The list in +# the variable named ${prerequisites_var} should be empty on first entry to +# this function. On exit, ${prerequisites_var} will contain the list of +# required shared library files. +# +# target is the full path to an executable file +# +# prerequisites_var is the name of a CMake variable to contain the results +# +# exclude_system is 0 or 1: 0 to include "system" prerequisites , 1 to +# exclude them +# +# recurse is 0 or 1: 0 for direct prerequisites only, 1 for all prerequisites +# recursively +# +# exepath is the path to the top level executable used for @executable_path +# replacment on the Mac +# +# dirs is a list of paths where libraries might be found: these paths are +# searched first when a target without any path info is given. Then standard +# system locations are also searched: PATH, Framework locations, /usr/lib... +# +function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs) + set(verbose 0) + set(eol_char "E") + + if(NOT IS_ABSOLUTE "${target}") + message("warning: target '${target}' is not absolute...") + endif(NOT IS_ABSOLUTE "${target}") + + if(NOT EXISTS "${target}") + message("warning: target '${target}' does not exist...") + endif(NOT EXISTS "${target}") + + # + # + # Try to choose the right tool by default. Caller can set gp_tool prior to + # calling this function to force using a different tool. + # + if("${gp_tool}" STREQUAL "") + set(gp_tool "ldd") + if(APPLE) + set(gp_tool "otool") + endif(APPLE) + if(WIN32) + set(gp_tool "dumpbin") + endif(WIN32) + endif("${gp_tool}" STREQUAL "") + + set(gp_tool_known 0) + + if("${gp_tool}" STREQUAL "ldd") + set(gp_cmd_args "") + set(gp_regex "^[\t ]*[^\t ]+ => ([^\t ]+).*${eol_char}$") + set(gp_regex_cmp_count 1) + set(gp_tool_known 1) + endif("${gp_tool}" STREQUAL "ldd") + + if("${gp_tool}" STREQUAL "otool") + set(gp_cmd_args "-L") + set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$") + set(gp_regex_cmp_count 3) + set(gp_tool_known 1) + endif("${gp_tool}" STREQUAL "otool") + + if("${gp_tool}" STREQUAL "dumpbin") + set(gp_cmd_args "/dependents") + set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$") + set(gp_regex_cmp_count 1) + set(gp_tool_known 1) + set(ENV{VS_UNICODE_OUTPUT} "") # Block extra output from inside VS IDE. + endif("${gp_tool}" STREQUAL "dumpbin") + + if(NOT gp_tool_known) + message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...") + message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'") + message(STATUS "Valid gp_tool values are dumpbin, ldd and otool.") + return() + endif(NOT gp_tool_known) + + set(gp_cmd_paths ${gp_cmd_paths} + "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin" + "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin" + "C:/Program Files/Microsoft Visual Studio 8/VC/BIN" + "C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN" + "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN" + "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN" + "/usr/local/bin" + "/usr/bin" + ) + + find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths}) + + if(NOT gp_cmd) + message(STATUS "warning: could not find '${gp_tool}' - cannot analyze prerequisites...") + return() + endif(NOT gp_cmd) + + if("${gp_tool}" STREQUAL "dumpbin") + # When running dumpbin, it also needs the "Common7/IDE" directory in the + # PATH. It will already be in the PATH if being run from a Visual Studio + # command prompt. Add it to the PATH here in case we are running from a + # different command prompt. + # + get_filename_component(gp_cmd_dir "${gp_cmd}" PATH) + get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE) + if(EXISTS "${gp_cmd_dlls_dir}") + # only add to the path if it is not already in the path + if(NOT "$ENV{PATH}" MATCHES "${gp_cmd_dlls_dir}") + set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}") + endif(NOT "$ENV{PATH}" MATCHES "${gp_cmd_dlls_dir}") + endif(EXISTS "${gp_cmd_dlls_dir}") + endif("${gp_tool}" STREQUAL "dumpbin") + # + # + + if("${gp_tool}" STREQUAL "ldd") + set(old_ld_env "$ENV{LD_LIBRARY_PATH}") + foreach(dir ${exepath} ${dirs}) + set(ENV{LD_LIBRARY_PATH} "${dir}:$ENV{LD_LIBRARY_PATH}") + endforeach(dir) + endif("${gp_tool}" STREQUAL "ldd") + + + # Track new prerequisites at each new level of recursion. Start with an + # empty list at each level: + # + set(unseen_prereqs) + + # Run gp_cmd on the target: + # + execute_process( + COMMAND ${gp_cmd} ${gp_cmd_args} ${target} + OUTPUT_VARIABLE gp_cmd_ov + ) + + if("${gp_tool}" STREQUAL "ldd") + set(ENV{LD_LIBRARY_PATH} "${old_ld_env}") + endif("${gp_tool}" STREQUAL "ldd") + + if(verbose) + message(STATUS "") + message(STATUS "gp_cmd_ov='${gp_cmd_ov}'") + message(STATUS "") + endif(verbose) + + get_filename_component(target_dir "${target}" PATH) + + # Convert to a list of lines: + # + string(REGEX REPLACE ";" "\\\\;" candidates "${gp_cmd_ov}") + string(REGEX REPLACE "\n" "${eol_char};" candidates "${candidates}") + + # Analyze each line for file names that match the regular expression: + # + foreach(candidate ${candidates}) + if("${candidate}" MATCHES "${gp_regex}") + # Extract information from each candidate: + string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}") + + if(gp_regex_cmp_count GREATER 1) + string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}") + string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}") + string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}") + string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}") + endif(gp_regex_cmp_count GREATER 1) + + if(gp_regex_cmp_count GREATER 2) + string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}") + string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}") + string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}") + string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}") + endif(gp_regex_cmp_count GREATER 2) + + # Use the raw_item as the list entries returned by this function. Use the + # gp_resolve_item function to resolve it to an actual full path file if + # necessary. + # + set(item "${raw_item}") + + # Add each item unless it is excluded: + # + set(add_item 1) + + if(${exclude_system}) + set(type "") + gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type) + if("${type}" STREQUAL "system") + set(add_item 0) + endif("${type}" STREQUAL "system") + endif(${exclude_system}) + + if(add_item) + list(LENGTH ${prerequisites_var} list_length_before_append) + gp_append_unique(${prerequisites_var} "${item}") + list(LENGTH ${prerequisites_var} list_length_after_append) + + if(${recurse}) + # If item was really added, this is the first time we have seen it. + # Add it to unseen_prereqs so that we can recursively add *its* + # prerequisites... + # + # But first: resolve its name to an absolute full path name such + # that the analysis tools can simply accept it as input. + # + if(NOT list_length_before_append EQUAL list_length_after_append) + gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item) + set(unseen_prereqs ${unseen_prereqs} "${resolved_item}") + endif(NOT list_length_before_append EQUAL list_length_after_append) + endif(${recurse}) + endif(add_item) + else("${candidate}" MATCHES "${gp_regex}") + if(verbose) + message(STATUS "ignoring non-matching line: '${candidate}'") + endif(verbose) + endif("${candidate}" MATCHES "${gp_regex}") + endforeach(candidate) + + list(LENGTH ${prerequisites_var} prerequisites_var_length) + if(prerequisites_var_length GREATER 0) + list(SORT ${prerequisites_var}) + endif(prerequisites_var_length GREATER 0) + if(${recurse}) + set(more_inputs ${unseen_prereqs}) + foreach(input ${more_inputs}) + get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}") + endforeach(input) + endif(${recurse}) + + set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE) +endfunction(get_prerequisites) + + +# list_prerequisites target all exclude_system verbose +# +# ARGV0 (target) is the full path to an executable file +# +# optional ARGV1 (all) is 0 or 1: 0 for direct prerequisites only, +# 1 for all prerequisites recursively +# +# optional ARGV2 (exclude_system) is 0 or 1: 0 to include "system" +# prerequisites , 1 to exclude them +# +# optional ARGV3 (verbose) is 0 or 1: 0 to print only full path +# names of prerequisites, 1 to print extra information +# +function(list_prerequisites target) + if("${ARGV1}" STREQUAL "") + set(all 1) + else("${ARGV1}" STREQUAL "") + set(all "${ARGV1}") + endif("${ARGV1}" STREQUAL "") + + if("${ARGV2}" STREQUAL "") + set(exclude_system 0) + else("${ARGV2}" STREQUAL "") + set(exclude_system "${ARGV2}") + endif("${ARGV2}" STREQUAL "") + + if("${ARGV3}" STREQUAL "") + set(verbose 0) + else("${ARGV3}" STREQUAL "") + set(verbose "${ARGV3}") + endif("${ARGV3}" STREQUAL "") + + set(count 0) + set(count_str "") + set(print_count "${verbose}") + set(print_prerequisite_type "${verbose}") + set(print_target "${verbose}") + set(type_str "") + + get_filename_component(exepath "${target}" PATH) + + set(prereqs "") + get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "") + + if(print_target) + message(STATUS "File '${target}' depends on:") + endif(print_target) + + foreach(d ${prereqs}) + math(EXPR count "${count} + 1") + + if(print_count) + set(count_str "${count}. ") + endif(print_count) + + if(print_prerequisite_type) + gp_file_type("${target}" "${d}" type) + set(type_str " (${type})") + endif(print_prerequisite_type) + + message(STATUS "${count_str}${d}${type_str}") + endforeach(d) +endfunction(list_prerequisites) + + +# list_prerequisites_by_glob glob_arg glob_exp +# +# glob_arg is GLOB or GLOB_RECURSE +# +# glob_exp is a globbing expression used with "file(GLOB" to retrieve a list +# of matching files. If a matching file is executable, its prerequisites are +# listed. +# +# Any additional (optional) arguments provided are passed along as the +# optional arguments to the list_prerequisites calls. +# +function(list_prerequisites_by_glob glob_arg glob_exp) + message(STATUS "=============================================================================") + message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'") + message(STATUS "") + file(${glob_arg} file_list ${glob_exp}) + foreach(f ${file_list}) + is_file_executable("${f}" is_f_executable) + if(is_f_executable) + message(STATUS "=============================================================================") + list_prerequisites("${f}" ${ARGN}) + message(STATUS "") + endif(is_f_executable) + endforeach(f) +endfunction(list_prerequisites_by_glob) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 62b764bb30..05f0492234 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -1,276 +1,276 @@ -# -*- cmake -*- -include(LLTestCommand) -include(GoogleMock) - -MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) - # Given a project name and a list of sourcefiles (with optional properties on each), - # add targets to build and run the tests specified. - # ASSUMPTIONS: - # * this macro is being executed in the project file that is passed in - # * current working SOURCE dir is that project dir - # * there is a subfolder tests/ with test code corresponding to the filenames passed in - # * properties for each sourcefile passed in indicate what libs to link that file with (MAKE NO ASSUMPTIONS ASIDE FROM TUT) - # - # More info and examples at: https://wiki.secondlife.com/wiki/How_to_add_unit_tests_to_indra_code - # - # WARNING: do NOT modify this code without working with poppy - - # there is another branch that will conflict heavily with any changes here. -INCLUDE(GoogleMock) - - - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}") - ENDIF(LL_TEST_VERBOSE) - - # Start with the header and project-wide setup before making targets - #project(UNITTEST_PROJECT_${project}) - # Setup includes, paths, etc - SET(alltest_SOURCE_FILES - ${CMAKE_SOURCE_DIR}/test/test.cpp - ${CMAKE_SOURCE_DIR}/test/lltut.cpp - ) - SET(alltest_DEP_TARGETS - # needed by the test harness itself - ${APRUTIL_LIBRARIES} - ${APR_LIBRARIES} - llcommon - ) - IF(NOT "${project}" STREQUAL "llmath") - # add llmath as a dep unless the tested module *is* llmath! - LIST(APPEND alltest_DEP_TARGETS - llmath - ) - ENDIF(NOT "${project}" STREQUAL "llmath") - SET(alltest_INCLUDE_DIRS - ${LLMATH_INCLUDE_DIRS} - ${LLCOMMON_INCLUDE_DIRS} - ${LIBS_OPEN_DIR}/test - ${GOOGLEMOCK_INCLUDE_DIRS} - ) - SET(alltest_LIBRARIES - ${GOOGLEMOCK_LIBRARIES} - ${PTHREAD_LIBRARY} - ${WINDOWS_LIBRARIES} - ) - # Headers, for convenience in targets. - SET(alltest_HEADER_FILES - ${CMAKE_SOURCE_DIR}/test/test.h - ) - - # Use the default flags - if (LINUX) - SET(CMAKE_EXE_LINKER_FLAGS "") - endif (LINUX) - - # start the source test executable definitions - SET(${project}_TEST_OUTPUT "") - FOREACH (source ${sources}) - STRING( REGEX REPLACE "(.*)\\.[^.]+$" "\\1" name ${source} ) - STRING( REGEX REPLACE ".*\\.([^.]+)$" "\\1" extension ${source} ) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} individual source: ${source} (${name}.${extension})") - ENDIF(LL_TEST_VERBOSE) - - # - # Per-codefile additional / external source, header, and include dir property extraction - # - # Source - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_SOURCE_FILES ${source} LL_TEST_ADDITIONAL_SOURCE_FILES) - IF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND) - SET(${name}_test_additional_SOURCE_FILES "") - ENDIF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND) - SET(${name}_test_SOURCE_FILES ${source} tests/${name}_test.${extension} ${alltest_SOURCE_FILES} ${${name}_test_additional_SOURCE_FILES} ) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_SOURCE_FILES ${${name}_test_SOURCE_FILES}") - ENDIF(LL_TEST_VERBOSE) - # Headers - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_HEADER_FILES ${source} LL_TEST_ADDITIONAL_HEADER_FILES) - IF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND) - SET(${name}_test_additional_HEADER_FILES "") - ENDIF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND) - SET(${name}_test_HEADER_FILES ${name}.h ${${name}_test_additional_HEADER_FILES}) - set_source_files_properties(${${name}_test_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) - LIST(APPEND ${name}_test_SOURCE_FILES ${${name}_test_HEADER_FILES}) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_HEADER_FILES ${${name}_test_HEADER_FILES}") - ENDIF(LL_TEST_VERBOSE) - # Include dirs - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_INCLUDE_DIRS ${source} LL_TEST_ADDITIONAL_INCLUDE_DIRS) - IF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND) - SET(${name}_test_additional_INCLUDE_DIRS "") - ENDIF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND) - INCLUDE_DIRECTORIES(${alltest_INCLUDE_DIRS} ${name}_test_additional_INCLUDE_DIRS ) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}") - ENDIF(LL_TEST_VERBOSE) - - - # Setup target - ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES}) - SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") - - # - # Per-codefile additional / external project dep and lib dep property extraction - # - # WARNING: it's REALLY IMPORTANT to not mix these. I guarantee it will not work in the future. + poppy 2009-04-19 - # Projects - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_PROJECTS ${source} LL_TEST_ADDITIONAL_PROJECTS) - IF(${name}_test_additional_PROJECTS MATCHES NOTFOUND) - SET(${name}_test_additional_PROJECTS "") - ENDIF(${name}_test_additional_PROJECTS MATCHES NOTFOUND) - # Libraries - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_LIBRARIES ${source} LL_TEST_ADDITIONAL_LIBRARIES) - IF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND) - SET(${name}_test_additional_LIBRARIES "") - ENDIF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_PROJECTS ${${name}_test_additional_PROJECTS}") - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_LIBRARIES ${${name}_test_additional_LIBRARIES}") - ENDIF(LL_TEST_VERBOSE) - # Add to project - TARGET_LINK_LIBRARIES(PROJECT_${project}_TEST_${name} ${alltest_LIBRARIES} ${alltest_DEP_TARGETS} ${${name}_test_additional_PROJECTS} ${${name}_test_additional_LIBRARIES} ) - # Compile-time Definitions - GET_SOURCE_FILE_PROPERTY(${name}_test_additional_CFLAGS ${source} LL_TEST_ADDITIONAL_CFLAGS) - IF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND) - SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES COMPILE_FLAGS ${${name}_test_additional_CFLAGS} ) - IF(LL_TEST_VERBOSE) - MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_CFLAGS ${${name}_test_additional_CFLAGS}") - ENDIF(LL_TEST_VERBOSE) - ENDIF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND) - - # - # Setup test targets - # - GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION) - SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt) - SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR}) - - # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19 - IF(LL_TEST_VERBOSE) - MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd = ${TEST_CMD}") - ENDIF(LL_TEST_VERBOSE) - - SET_TEST_PATH(LD_LIBRARY_PATH) - LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${TEST_CMD}) - IF(LL_TEST_VERBOSE) - MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script = ${TEST_SCRIPT_CMD}") - ENDIF(LL_TEST_VERBOSE) - # Add test - ADD_CUSTOM_COMMAND( - OUTPUT ${TEST_OUTPUT} - COMMAND ${TEST_SCRIPT_CMD} - DEPENDS PROJECT_${project}_TEST_${name} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - # Why not add custom target and add POST_BUILD command? - # Slightly less uncertain behavior - # (OUTPUT commands run non-deterministically AFAIK) + poppy 2009-04-19 - # > I did not use a post build step as I could not make it notify of a - # > failure after the first time you build and fail a test. - daveh 2009-04-20 - LIST(APPEND ${project}_TEST_OUTPUT ${TEST_OUTPUT}) - ENDFOREACH (source) - - # Add the test runner target per-project - # (replaces old _test_ok targets all over the place) - ADD_CUSTOM_TARGET(${project}_tests ALL DEPENDS ${${project}_TEST_OUTPUT}) - ADD_DEPENDENCIES(${project} ${project}_tests) -ENDMACRO(LL_ADD_PROJECT_UNIT_TESTS) - -FUNCTION(LL_ADD_INTEGRATION_TEST - testname - additional_source_files - library_dependencies -# variable args - ) - if(TEST_DEBUG) - message(STATUS "Adding INTEGRATION_TEST_${testname} - debug output is on") - endif(TEST_DEBUG) - - SET(source_files - tests/${testname}_test.cpp - ${CMAKE_SOURCE_DIR}/test/test.cpp - ${CMAKE_SOURCE_DIR}/test/lltut.cpp - ${additional_source_files} - ) - - SET(libraries - ${library_dependencies} - ${GOOGLEMOCK_LIBRARIES} - ${PTHREAD_LIBRARY} - ) - - # Add test executable build target - if(TEST_DEBUG) - message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})") - endif(TEST_DEBUG) - ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files}) - SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") - if(STANDALONE) - SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES COMPILE_FLAGS -I"${TUT_INCLUDE_DIR}") - endif(STANDALONE) - - # Add link deps to the executable - if(TEST_DEBUG) - message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})") - endif(TEST_DEBUG) - TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries}) - - # Create the test running command - SET(test_command ${ARGN}) - GET_TARGET_PROPERTY(TEST_EXE INTEGRATION_TEST_${testname} LOCATION) - LIST(FIND test_command "{}" test_exe_pos) - IF(test_exe_pos LESS 0) - # The {} marker means "the full pathname of the test executable." - # test_exe_pos -1 means we didn't find it -- so append the test executable - # name to $ARGN, the variable part of the arg list. This is convenient - # shorthand for both straightforward execution of the test program (empty - # $ARGN) and for running a "wrapper" program of some kind accepting the - # pathname of the test program as the last of its args. You need specify - # {} only if the test program's pathname isn't the last argument in the - # desired command line. - LIST(APPEND test_command "${TEST_EXE}") - ELSE (test_exe_pos LESS 0) - # Found {} marker at test_exe_pos. Remove the {}... - LIST(REMOVE_AT test_command test_exe_pos) - # ...and replace it with the actual name of the test executable. - LIST(INSERT test_command test_exe_pos "${TEST_EXE}") - ENDIF (test_exe_pos LESS 0) - - SET_TEST_PATH(LD_LIBRARY_PATH) - LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${test_command}) - - if(TEST_DEBUG) - message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}") - endif(TEST_DEBUG) - - ADD_CUSTOM_COMMAND( - TARGET INTEGRATION_TEST_${testname} - POST_BUILD - COMMAND ${TEST_SCRIPT_CMD} - ) - - # Use CTEST? Not sure how to yet... - # ADD_TEST(INTEGRATION_TEST_RUNNER_${testname} ${TEST_SCRIPT_CMD}) - -ENDFUNCTION(LL_ADD_INTEGRATION_TEST) - -MACRO(SET_TEST_PATH LISTVAR) - IF(WINDOWS) - # We typically build/package only Release variants of third-party - # libraries, so append the Release staging dir in case the library being - # sought doesn't have a debug variant. - set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} ${SHARED_LIB_STAGING_DIR}/Release) - ELSEIF(DARWIN) - # We typically build/package only Release variants of third-party - # libraries, so append the Release staging dir in case the library being - # sought doesn't have a debug variant. - set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources ${SHARED_LIB_STAGING_DIR}/Release/Resources /usr/lib) - ELSE(WINDOWS) - # Linux uses a single staging directory anyway. - IF (STANDALONE) - set(${LISTVAR} ${CMAKE_BINARY_DIR}/llcommon /usr/lib /usr/local/lib) - ELSE (STANDALONE) - set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib) - ENDIF (STANDALONE) - ENDIF(WINDOWS) -ENDMACRO(SET_TEST_PATH) +# -*- cmake -*- +include(LLTestCommand) +include(GoogleMock) + +MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) + # Given a project name and a list of sourcefiles (with optional properties on each), + # add targets to build and run the tests specified. + # ASSUMPTIONS: + # * this macro is being executed in the project file that is passed in + # * current working SOURCE dir is that project dir + # * there is a subfolder tests/ with test code corresponding to the filenames passed in + # * properties for each sourcefile passed in indicate what libs to link that file with (MAKE NO ASSUMPTIONS ASIDE FROM TUT) + # + # More info and examples at: https://wiki.secondlife.com/wiki/How_to_add_unit_tests_to_indra_code + # + # WARNING: do NOT modify this code without working with poppy - + # there is another branch that will conflict heavily with any changes here. +INCLUDE(GoogleMock) + + + IF(LL_TEST_VERBOSE) + MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}") + ENDIF(LL_TEST_VERBOSE) + + # Start with the header and project-wide setup before making targets + #project(UNITTEST_PROJECT_${project}) + # Setup includes, paths, etc + SET(alltest_SOURCE_FILES + ${CMAKE_SOURCE_DIR}/test/test.cpp + ${CMAKE_SOURCE_DIR}/test/lltut.cpp + ) + SET(alltest_DEP_TARGETS + # needed by the test harness itself + ${APRUTIL_LIBRARIES} + ${APR_LIBRARIES} + llcommon + ) + IF(NOT "${project}" STREQUAL "llmath") + # add llmath as a dep unless the tested module *is* llmath! + LIST(APPEND alltest_DEP_TARGETS + llmath + ) + ENDIF(NOT "${project}" STREQUAL "llmath") + SET(alltest_INCLUDE_DIRS + ${LLMATH_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LIBS_OPEN_DIR}/test + ${GOOGLEMOCK_INCLUDE_DIRS} + ) + SET(alltest_LIBRARIES + ${GOOGLEMOCK_LIBRARIES} + ${PTHREAD_LIBRARY} + ${WINDOWS_LIBRARIES} + ) + # Headers, for convenience in targets. + SET(alltest_HEADER_FILES + ${CMAKE_SOURCE_DIR}/test/test.h + ) + + # Use the default flags + if (LINUX) + SET(CMAKE_EXE_LINKER_FLAGS "") + endif (LINUX) + + # start the source test executable definitions + SET(${project}_TEST_OUTPUT "") + FOREACH (source ${sources}) + STRING( REGEX REPLACE "(.*)\\.[^.]+$" "\\1" name ${source} ) + STRING( REGEX REPLACE ".*\\.([^.]+)$" "\\1" extension ${source} ) + IF(LL_TEST_VERBOSE) + MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} individual source: ${source} (${name}.${extension})") + ENDIF(LL_TEST_VERBOSE) + + # + # Per-codefile additional / external source, header, and include dir property extraction + # + # Source + GET_SOURCE_FILE_PROPERTY(${name}_test_additional_SOURCE_FILES ${source} LL_TEST_ADDITIONAL_SOURCE_FILES) + IF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND) + SET(${name}_test_additional_SOURCE_FILES "") + ENDIF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND) + SET(${name}_test_SOURCE_FILES ${source} tests/${name}_test.${extension} ${alltest_SOURCE_FILES} ${${name}_test_additional_SOURCE_FILES} ) + IF(LL_TEST_VERBOSE) + MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_SOURCE_FILES ${${name}_test_SOURCE_FILES}") + ENDIF(LL_TEST_VERBOSE) + # Headers + GET_SOURCE_FILE_PROPERTY(${name}_test_additional_HEADER_FILES ${source} LL_TEST_ADDITIONAL_HEADER_FILES) + IF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND) + SET(${name}_test_additional_HEADER_FILES "") + ENDIF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND) + SET(${name}_test_HEADER_FILES ${name}.h ${${name}_test_additional_HEADER_FILES}) + set_source_files_properties(${${name}_test_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) + LIST(APPEND ${name}_test_SOURCE_FILES ${${name}_test_HEADER_FILES}) + IF(LL_TEST_VERBOSE) + MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_HEADER_FILES ${${name}_test_HEADER_FILES}") + ENDIF(LL_TEST_VERBOSE) + # Include dirs + GET_SOURCE_FILE_PROPERTY(${name}_test_additional_INCLUDE_DIRS ${source} LL_TEST_ADDITIONAL_INCLUDE_DIRS) + IF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND) + SET(${name}_test_additional_INCLUDE_DIRS "") + ENDIF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND) + INCLUDE_DIRECTORIES(${alltest_INCLUDE_DIRS} ${name}_test_additional_INCLUDE_DIRS ) + IF(LL_TEST_VERBOSE) + MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}") + ENDIF(LL_TEST_VERBOSE) + + + # Setup target + ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES}) + SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") + + # + # Per-codefile additional / external project dep and lib dep property extraction + # + # WARNING: it's REALLY IMPORTANT to not mix these. I guarantee it will not work in the future. + poppy 2009-04-19 + # Projects + GET_SOURCE_FILE_PROPERTY(${name}_test_additional_PROJECTS ${source} LL_TEST_ADDITIONAL_PROJECTS) + IF(${name}_test_additional_PROJECTS MATCHES NOTFOUND) + SET(${name}_test_additional_PROJECTS "") + ENDIF(${name}_test_additional_PROJECTS MATCHES NOTFOUND) + # Libraries + GET_SOURCE_FILE_PROPERTY(${name}_test_additional_LIBRARIES ${source} LL_TEST_ADDITIONAL_LIBRARIES) + IF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND) + SET(${name}_test_additional_LIBRARIES "") + ENDIF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND) + IF(LL_TEST_VERBOSE) + MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_PROJECTS ${${name}_test_additional_PROJECTS}") + MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_LIBRARIES ${${name}_test_additional_LIBRARIES}") + ENDIF(LL_TEST_VERBOSE) + # Add to project + TARGET_LINK_LIBRARIES(PROJECT_${project}_TEST_${name} ${alltest_LIBRARIES} ${alltest_DEP_TARGETS} ${${name}_test_additional_PROJECTS} ${${name}_test_additional_LIBRARIES} ) + # Compile-time Definitions + GET_SOURCE_FILE_PROPERTY(${name}_test_additional_CFLAGS ${source} LL_TEST_ADDITIONAL_CFLAGS) + IF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND) + SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES COMPILE_FLAGS ${${name}_test_additional_CFLAGS} ) + IF(LL_TEST_VERBOSE) + MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_CFLAGS ${${name}_test_additional_CFLAGS}") + ENDIF(LL_TEST_VERBOSE) + ENDIF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND) + + # + # Setup test targets + # + GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION) + SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt) + SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR}) + + # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19 + IF(LL_TEST_VERBOSE) + MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd = ${TEST_CMD}") + ENDIF(LL_TEST_VERBOSE) + + SET_TEST_PATH(LD_LIBRARY_PATH) + LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${TEST_CMD}) + IF(LL_TEST_VERBOSE) + MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script = ${TEST_SCRIPT_CMD}") + ENDIF(LL_TEST_VERBOSE) + # Add test + ADD_CUSTOM_COMMAND( + OUTPUT ${TEST_OUTPUT} + COMMAND ${TEST_SCRIPT_CMD} + DEPENDS PROJECT_${project}_TEST_${name} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + # Why not add custom target and add POST_BUILD command? + # Slightly less uncertain behavior + # (OUTPUT commands run non-deterministically AFAIK) + poppy 2009-04-19 + # > I did not use a post build step as I could not make it notify of a + # > failure after the first time you build and fail a test. - daveh 2009-04-20 + LIST(APPEND ${project}_TEST_OUTPUT ${TEST_OUTPUT}) + ENDFOREACH (source) + + # Add the test runner target per-project + # (replaces old _test_ok targets all over the place) + ADD_CUSTOM_TARGET(${project}_tests ALL DEPENDS ${${project}_TEST_OUTPUT}) + ADD_DEPENDENCIES(${project} ${project}_tests) +ENDMACRO(LL_ADD_PROJECT_UNIT_TESTS) + +FUNCTION(LL_ADD_INTEGRATION_TEST + testname + additional_source_files + library_dependencies +# variable args + ) + if(TEST_DEBUG) + message(STATUS "Adding INTEGRATION_TEST_${testname} - debug output is on") + endif(TEST_DEBUG) + + SET(source_files + tests/${testname}_test.cpp + ${CMAKE_SOURCE_DIR}/test/test.cpp + ${CMAKE_SOURCE_DIR}/test/lltut.cpp + ${additional_source_files} + ) + + SET(libraries + ${library_dependencies} + ${GOOGLEMOCK_LIBRARIES} + ${PTHREAD_LIBRARY} + ) + + # Add test executable build target + if(TEST_DEBUG) + message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})") + endif(TEST_DEBUG) + ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files}) + SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") + if(STANDALONE) + SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES COMPILE_FLAGS -I"${TUT_INCLUDE_DIR}") + endif(STANDALONE) + + # Add link deps to the executable + if(TEST_DEBUG) + message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})") + endif(TEST_DEBUG) + TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries}) + + # Create the test running command + SET(test_command ${ARGN}) + GET_TARGET_PROPERTY(TEST_EXE INTEGRATION_TEST_${testname} LOCATION) + LIST(FIND test_command "{}" test_exe_pos) + IF(test_exe_pos LESS 0) + # The {} marker means "the full pathname of the test executable." + # test_exe_pos -1 means we didn't find it -- so append the test executable + # name to $ARGN, the variable part of the arg list. This is convenient + # shorthand for both straightforward execution of the test program (empty + # $ARGN) and for running a "wrapper" program of some kind accepting the + # pathname of the test program as the last of its args. You need specify + # {} only if the test program's pathname isn't the last argument in the + # desired command line. + LIST(APPEND test_command "${TEST_EXE}") + ELSE (test_exe_pos LESS 0) + # Found {} marker at test_exe_pos. Remove the {}... + LIST(REMOVE_AT test_command test_exe_pos) + # ...and replace it with the actual name of the test executable. + LIST(INSERT test_command test_exe_pos "${TEST_EXE}") + ENDIF (test_exe_pos LESS 0) + + SET_TEST_PATH(LD_LIBRARY_PATH) + LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${test_command}) + + if(TEST_DEBUG) + message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}") + endif(TEST_DEBUG) + + ADD_CUSTOM_COMMAND( + TARGET INTEGRATION_TEST_${testname} + POST_BUILD + COMMAND ${TEST_SCRIPT_CMD} + ) + + # Use CTEST? Not sure how to yet... + # ADD_TEST(INTEGRATION_TEST_RUNNER_${testname} ${TEST_SCRIPT_CMD}) + +ENDFUNCTION(LL_ADD_INTEGRATION_TEST) + +MACRO(SET_TEST_PATH LISTVAR) + IF(WINDOWS) + # We typically build/package only Release variants of third-party + # libraries, so append the Release staging dir in case the library being + # sought doesn't have a debug variant. + set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} ${SHARED_LIB_STAGING_DIR}/Release) + ELSEIF(DARWIN) + # We typically build/package only Release variants of third-party + # libraries, so append the Release staging dir in case the library being + # sought doesn't have a debug variant. + set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources ${SHARED_LIB_STAGING_DIR}/Release/Resources /usr/lib) + ELSE(WINDOWS) + # Linux uses a single staging directory anyway. + IF (STANDALONE) + set(${LISTVAR} ${CMAKE_BINARY_DIR}/llcommon /usr/lib /usr/local/lib) + ELSE (STANDALONE) + set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib) + ENDIF (STANDALONE) + ENDIF(WINDOWS) +ENDMACRO(SET_TEST_PATH) diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp index 51726112a0..058567492b 100644 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -1,402 +1,402 @@ -/** - * @file llfloaterwebcontent.cpp - * @brief floater for displaying web content - e.g. profiles and search (eventually) - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llcombobox.h" -#include "lliconctrl.h" -#include "llfloaterreg.h" -#include "lllayoutstack.h" -#include "llpluginclassmedia.h" -#include "llprogressbar.h" -#include "lltextbox.h" -#include "llurlhistory.h" -#include "llviewercontrol.h" -#include "llweb.h" -#include "llwindow.h" - -#include "llfloaterwebcontent.h" - -LLFloaterWebContent::LLFloaterWebContent( const LLSD& key ) - : LLFloater( key ) -{ - mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this )); - mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this )); - mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this )); - mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this )); - mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this )); - mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this )); -} - -BOOL LLFloaterWebContent::postBuild() -{ - // these are used in a bunch of places so cache them - mWebBrowser = getChild< LLMediaCtrl >( "webbrowser" ); - mAddressCombo = getChild< LLComboBox >( "address" ); - mStatusBarText = getChild< LLTextBox >( "statusbartext" ); - mStatusBarProgress = getChild("statusbarprogress" ); - - // observe browser events - mWebBrowser->addObserver( this ); - - // these buttons are always enabled - getChildView("reload")->setEnabled( true ); - getChildView("popexternal")->setEnabled( true ); - - // cache image for secure browsing - mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag"); - - // initialize the URL history using the system URL History manager - initializeURLHistory(); - - return TRUE; -} - -void LLFloaterWebContent::initializeURLHistory() -{ - // start with an empty list - LLCtrlListInterface* url_list = childGetListInterface("address"); - if (url_list) - { - url_list->operateOnAll(LLCtrlListInterface::OP_DELETE); - } - - // Get all of the entries in the "browser" collection - LLSD browser_history = LLURLHistory::getURLHistory("browser"); - LLSD::array_iterator iter_history = - browser_history.beginArray(); - LLSD::array_iterator end_history = - browser_history.endArray(); - for(; iter_history != end_history; ++iter_history) - { - std::string url = (*iter_history).asString(); - if(! url.empty()) - url_list->addSimpleElement(url); - } -} - -//static -void LLFloaterWebContent::create( const std::string &url, const std::string& target, const std::string& uuid ) -{ - lldebugs << "url = " << url << ", target = " << target << ", uuid = " << uuid << llendl; - - std::string tag = target; - - if(target.empty() || target == "_blank") - { - if(!uuid.empty()) - { - tag = uuid; - } - else - { - // create a unique tag for this instance - LLUUID id; - id.generate(); - tag = id.asString(); - } - } - - S32 browser_window_limit = gSavedSettings.getS32("WebContentWindowLimit"); - - if(LLFloaterReg::findInstance("web_content", tag) != NULL) - { - // There's already a web browser for this tag, so we won't be opening a new window. - } - else if(browser_window_limit != 0) - { - // showInstance will open a new window. Figure out how many web browsers are already open, - // and close the least recently opened one if this will put us over the limit. - - LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList("web_content"); - lldebugs << "total instance count is " << instances.size() << llendl; - - for(LLFloaterReg::const_instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); iter++) - { - lldebugs << " " << (*iter)->getKey() << llendl; - } - - if(instances.size() >= (size_t)browser_window_limit) - { - // Destroy the least recently opened instance - (*instances.begin())->closeFloater(); - } - } - - LLFloaterWebContent *browser = dynamic_cast (LLFloaterReg::showInstance("web_content", tag)); - llassert(browser); - if(browser) - { - browser->mUUID = uuid; - - // tell the browser instance to load the specified URL - browser->open_media(url, target); - LLViewerMedia::proxyWindowOpened(target, uuid); - } -} - -//static -void LLFloaterWebContent::closeRequest(const std::string &uuid) -{ - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content"); - lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl; - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) - { - LLFloaterWebContent* i = dynamic_cast(*iter); - lldebugs << " " << i->mUUID << llendl; - if (i && i->mUUID == uuid) - { - i->closeFloater(false); - return; - } - } -} - -//static -void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height) -{ - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content"); - lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl; - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) - { - LLFloaterWebContent* i = dynamic_cast(*iter); - lldebugs << " " << i->mUUID << llendl; - if (i && i->mUUID == uuid) - { - i->geometryChanged(x, y, width, height); - return; - } - } -} - -void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) -{ - // Make sure the layout of the browser control is updated, so this calculation is correct. - LLLayoutStack::updateClass(); - - // TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc. - LLCoordWindow window_size; - getWindow()->getSize(&window_size); - - // Adjust width and height for the size of the chrome on the web Browser window. - width += getRect().getWidth() - mWebBrowser->getRect().getWidth(); - height += getRect().getHeight() - mWebBrowser->getRect().getHeight(); - - LLRect geom; - geom.setOriginAndSize(x, window_size.mY - (y + height), width, height); - - lldebugs << "geometry change: " << geom << llendl; - - handleReshape(geom,false); -} - -void LLFloaterWebContent::open_media(const std::string& web_url, const std::string& target) -{ - // Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin. - mWebBrowser->setHomePageUrl(web_url, "text/html"); - mWebBrowser->setTarget(target); - mWebBrowser->navigateTo(web_url, "text/html"); - set_current_url(web_url); -} - -//virtual -void LLFloaterWebContent::onClose(bool app_quitting) -{ - LLViewerMedia::proxyWindowClosed(mUUID); - destroy(); -} - -// virtual -void LLFloaterWebContent::draw() -{ - // this is asychronous so we need to keep checking - getChildView( "back" )->setEnabled( mWebBrowser->canNavigateBack() ); - getChildView( "forward" )->setEnabled( mWebBrowser->canNavigateForward() ); - - LLFloater::draw(); -} - -// virtual -void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) -{ - if(event == MEDIA_EVENT_LOCATION_CHANGED) - { - const std::string url = self->getLocation(); - - if ( url.length() ) - mStatusBarText->setText( url ); - - set_current_url( url ); - } - else if(event == MEDIA_EVENT_NAVIGATE_BEGIN) - { - // flags are sent with this event - getChildView("back")->setEnabled( self->getHistoryBackAvailable() ); - getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() ); - - // toggle visibility of these buttons based on browser state - getChildView("reload")->setVisible( false ); - getChildView("stop")->setVisible( true ); - - // turn "on" progress bar now we're about to start loading - mStatusBarProgress->setVisible( true ); - } - else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) - { - // flags are sent with this event - getChildView("back")->setEnabled( self->getHistoryBackAvailable() ); - getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() ); - - // toggle visibility of these buttons based on browser state - getChildView("reload")->setVisible( true ); - getChildView("stop")->setVisible( false ); - - // turn "off" progress bar now we're loaded - mStatusBarProgress->setVisible( false ); - - // we populate the status bar with URLs as they change so clear it now we're done - const std::string end_str = ""; - mStatusBarText->setText( end_str ); - - // decide if secure browsing icon should be displayed - std::string prefix = std::string("https://"); - std::string test_prefix = mCurrentURL.substr(0, prefix.length()); - LLStringUtil::toLower(test_prefix); - if(test_prefix == prefix) - { - mSecureLockIcon->setVisible(true); - } - else - { - mSecureLockIcon->setVisible(false); - } - } - else if(event == MEDIA_EVENT_CLOSE_REQUEST) - { - // The browser instance wants its window closed. - closeFloater(); - } - else if(event == MEDIA_EVENT_GEOMETRY_CHANGE) - { - geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight()); - } - else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED ) - { - const std::string text = self->getStatusText(); - if ( text.length() ) - mStatusBarText->setText( text ); - } - else if(event == MEDIA_EVENT_PROGRESS_UPDATED ) - { - int percent = (int)self->getProgressPercent(); - mStatusBarProgress->setValue( percent ); - } - else if(event == MEDIA_EVENT_NAME_CHANGED ) - { - std::string page_title = self->getMediaName(); - // simulate browser behavior - title is empty, use the current URL - if ( page_title.length() > 0 ) - setTitle( page_title ); - else - setTitle( mCurrentURL ); - } - else if(event == MEDIA_EVENT_LINK_HOVERED ) - { - const std::string link = self->getHoverLink(); - mStatusBarText->setText( link ); - } -} - -void LLFloaterWebContent::set_current_url(const std::string& url) -{ - mCurrentURL = url; - - // serialize url history into the system URL History manager - LLURLHistory::removeURL("browser", mCurrentURL); - LLURLHistory::addURL("browser", mCurrentURL); - - mAddressCombo->remove( mCurrentURL ); - mAddressCombo->add( mCurrentURL ); - mAddressCombo->selectByValue( mCurrentURL ); -} - -void LLFloaterWebContent::onClickForward() -{ - mWebBrowser->navigateForward(); -} - -void LLFloaterWebContent::onClickBack() -{ - mWebBrowser->navigateBack(); -} - -void LLFloaterWebContent::onClickReload() -{ - - if( mWebBrowser->getMediaPlugin() ) - { - bool ignore_cache = true; - mWebBrowser->getMediaPlugin()->browse_reload( ignore_cache ); - } - else - { - mWebBrowser->navigateTo(mCurrentURL); - } -} - -void LLFloaterWebContent::onClickStop() -{ - if( mWebBrowser->getMediaPlugin() ) - mWebBrowser->getMediaPlugin()->browse_stop(); - - // still should happen when we catch the navigate complete event - // but sometimes (don't know why) that event isn't sent from Qt - // and we getto a point where the stop button stays active. - getChildView("reload")->setVisible( true ); - getChildView("stop")->setVisible( false ); -} - -void LLFloaterWebContent::onEnterAddress() -{ - // make sure there is at least something there. - // (perhaps this test should be for minimum length of a URL) - std::string url = mAddressCombo->getValue().asString(); - if ( url.length() > 0 ) - { - mWebBrowser->navigateTo( url, "text/html"); - }; -} - -void LLFloaterWebContent::onPopExternal() -{ - // make sure there is at least something there. - // (perhaps this test should be for minimum length of a URL) - std::string url = mAddressCombo->getValue().asString(); - if ( url.length() > 0 ) - { - LLWeb::loadURLExternal( url ); - }; -} +/** + * @file llfloaterwebcontent.cpp + * @brief floater for displaying web content - e.g. profiles and search (eventually) + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llcombobox.h" +#include "lliconctrl.h" +#include "llfloaterreg.h" +#include "lllayoutstack.h" +#include "llpluginclassmedia.h" +#include "llprogressbar.h" +#include "lltextbox.h" +#include "llurlhistory.h" +#include "llviewercontrol.h" +#include "llweb.h" +#include "llwindow.h" + +#include "llfloaterwebcontent.h" + +LLFloaterWebContent::LLFloaterWebContent( const LLSD& key ) + : LLFloater( key ) +{ + mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this )); + mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this )); + mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this )); + mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this )); + mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this )); + mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this )); +} + +BOOL LLFloaterWebContent::postBuild() +{ + // these are used in a bunch of places so cache them + mWebBrowser = getChild< LLMediaCtrl >( "webbrowser" ); + mAddressCombo = getChild< LLComboBox >( "address" ); + mStatusBarText = getChild< LLTextBox >( "statusbartext" ); + mStatusBarProgress = getChild("statusbarprogress" ); + + // observe browser events + mWebBrowser->addObserver( this ); + + // these buttons are always enabled + getChildView("reload")->setEnabled( true ); + getChildView("popexternal")->setEnabled( true ); + + // cache image for secure browsing + mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag"); + + // initialize the URL history using the system URL History manager + initializeURLHistory(); + + return TRUE; +} + +void LLFloaterWebContent::initializeURLHistory() +{ + // start with an empty list + LLCtrlListInterface* url_list = childGetListInterface("address"); + if (url_list) + { + url_list->operateOnAll(LLCtrlListInterface::OP_DELETE); + } + + // Get all of the entries in the "browser" collection + LLSD browser_history = LLURLHistory::getURLHistory("browser"); + LLSD::array_iterator iter_history = + browser_history.beginArray(); + LLSD::array_iterator end_history = + browser_history.endArray(); + for(; iter_history != end_history; ++iter_history) + { + std::string url = (*iter_history).asString(); + if(! url.empty()) + url_list->addSimpleElement(url); + } +} + +//static +void LLFloaterWebContent::create( const std::string &url, const std::string& target, const std::string& uuid ) +{ + lldebugs << "url = " << url << ", target = " << target << ", uuid = " << uuid << llendl; + + std::string tag = target; + + if(target.empty() || target == "_blank") + { + if(!uuid.empty()) + { + tag = uuid; + } + else + { + // create a unique tag for this instance + LLUUID id; + id.generate(); + tag = id.asString(); + } + } + + S32 browser_window_limit = gSavedSettings.getS32("WebContentWindowLimit"); + + if(LLFloaterReg::findInstance("web_content", tag) != NULL) + { + // There's already a web browser for this tag, so we won't be opening a new window. + } + else if(browser_window_limit != 0) + { + // showInstance will open a new window. Figure out how many web browsers are already open, + // and close the least recently opened one if this will put us over the limit. + + LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList("web_content"); + lldebugs << "total instance count is " << instances.size() << llendl; + + for(LLFloaterReg::const_instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); iter++) + { + lldebugs << " " << (*iter)->getKey() << llendl; + } + + if(instances.size() >= (size_t)browser_window_limit) + { + // Destroy the least recently opened instance + (*instances.begin())->closeFloater(); + } + } + + LLFloaterWebContent *browser = dynamic_cast (LLFloaterReg::showInstance("web_content", tag)); + llassert(browser); + if(browser) + { + browser->mUUID = uuid; + + // tell the browser instance to load the specified URL + browser->open_media(url, target); + LLViewerMedia::proxyWindowOpened(target, uuid); + } +} + +//static +void LLFloaterWebContent::closeRequest(const std::string &uuid) +{ + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content"); + lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl; + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + { + LLFloaterWebContent* i = dynamic_cast(*iter); + lldebugs << " " << i->mUUID << llendl; + if (i && i->mUUID == uuid) + { + i->closeFloater(false); + return; + } + } +} + +//static +void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height) +{ + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content"); + lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl; + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + { + LLFloaterWebContent* i = dynamic_cast(*iter); + lldebugs << " " << i->mUUID << llendl; + if (i && i->mUUID == uuid) + { + i->geometryChanged(x, y, width, height); + return; + } + } +} + +void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) +{ + // Make sure the layout of the browser control is updated, so this calculation is correct. + LLLayoutStack::updateClass(); + + // TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc. + LLCoordWindow window_size; + getWindow()->getSize(&window_size); + + // Adjust width and height for the size of the chrome on the web Browser window. + width += getRect().getWidth() - mWebBrowser->getRect().getWidth(); + height += getRect().getHeight() - mWebBrowser->getRect().getHeight(); + + LLRect geom; + geom.setOriginAndSize(x, window_size.mY - (y + height), width, height); + + lldebugs << "geometry change: " << geom << llendl; + + handleReshape(geom,false); +} + +void LLFloaterWebContent::open_media(const std::string& web_url, const std::string& target) +{ + // Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin. + mWebBrowser->setHomePageUrl(web_url, "text/html"); + mWebBrowser->setTarget(target); + mWebBrowser->navigateTo(web_url, "text/html"); + set_current_url(web_url); +} + +//virtual +void LLFloaterWebContent::onClose(bool app_quitting) +{ + LLViewerMedia::proxyWindowClosed(mUUID); + destroy(); +} + +// virtual +void LLFloaterWebContent::draw() +{ + // this is asychronous so we need to keep checking + getChildView( "back" )->setEnabled( mWebBrowser->canNavigateBack() ); + getChildView( "forward" )->setEnabled( mWebBrowser->canNavigateForward() ); + + LLFloater::draw(); +} + +// virtual +void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + if(event == MEDIA_EVENT_LOCATION_CHANGED) + { + const std::string url = self->getLocation(); + + if ( url.length() ) + mStatusBarText->setText( url ); + + set_current_url( url ); + } + else if(event == MEDIA_EVENT_NAVIGATE_BEGIN) + { + // flags are sent with this event + getChildView("back")->setEnabled( self->getHistoryBackAvailable() ); + getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() ); + + // toggle visibility of these buttons based on browser state + getChildView("reload")->setVisible( false ); + getChildView("stop")->setVisible( true ); + + // turn "on" progress bar now we're about to start loading + mStatusBarProgress->setVisible( true ); + } + else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) + { + // flags are sent with this event + getChildView("back")->setEnabled( self->getHistoryBackAvailable() ); + getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() ); + + // toggle visibility of these buttons based on browser state + getChildView("reload")->setVisible( true ); + getChildView("stop")->setVisible( false ); + + // turn "off" progress bar now we're loaded + mStatusBarProgress->setVisible( false ); + + // we populate the status bar with URLs as they change so clear it now we're done + const std::string end_str = ""; + mStatusBarText->setText( end_str ); + + // decide if secure browsing icon should be displayed + std::string prefix = std::string("https://"); + std::string test_prefix = mCurrentURL.substr(0, prefix.length()); + LLStringUtil::toLower(test_prefix); + if(test_prefix == prefix) + { + mSecureLockIcon->setVisible(true); + } + else + { + mSecureLockIcon->setVisible(false); + } + } + else if(event == MEDIA_EVENT_CLOSE_REQUEST) + { + // The browser instance wants its window closed. + closeFloater(); + } + else if(event == MEDIA_EVENT_GEOMETRY_CHANGE) + { + geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight()); + } + else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED ) + { + const std::string text = self->getStatusText(); + if ( text.length() ) + mStatusBarText->setText( text ); + } + else if(event == MEDIA_EVENT_PROGRESS_UPDATED ) + { + int percent = (int)self->getProgressPercent(); + mStatusBarProgress->setValue( percent ); + } + else if(event == MEDIA_EVENT_NAME_CHANGED ) + { + std::string page_title = self->getMediaName(); + // simulate browser behavior - title is empty, use the current URL + if ( page_title.length() > 0 ) + setTitle( page_title ); + else + setTitle( mCurrentURL ); + } + else if(event == MEDIA_EVENT_LINK_HOVERED ) + { + const std::string link = self->getHoverLink(); + mStatusBarText->setText( link ); + } +} + +void LLFloaterWebContent::set_current_url(const std::string& url) +{ + mCurrentURL = url; + + // serialize url history into the system URL History manager + LLURLHistory::removeURL("browser", mCurrentURL); + LLURLHistory::addURL("browser", mCurrentURL); + + mAddressCombo->remove( mCurrentURL ); + mAddressCombo->add( mCurrentURL ); + mAddressCombo->selectByValue( mCurrentURL ); +} + +void LLFloaterWebContent::onClickForward() +{ + mWebBrowser->navigateForward(); +} + +void LLFloaterWebContent::onClickBack() +{ + mWebBrowser->navigateBack(); +} + +void LLFloaterWebContent::onClickReload() +{ + + if( mWebBrowser->getMediaPlugin() ) + { + bool ignore_cache = true; + mWebBrowser->getMediaPlugin()->browse_reload( ignore_cache ); + } + else + { + mWebBrowser->navigateTo(mCurrentURL); + } +} + +void LLFloaterWebContent::onClickStop() +{ + if( mWebBrowser->getMediaPlugin() ) + mWebBrowser->getMediaPlugin()->browse_stop(); + + // still should happen when we catch the navigate complete event + // but sometimes (don't know why) that event isn't sent from Qt + // and we getto a point where the stop button stays active. + getChildView("reload")->setVisible( true ); + getChildView("stop")->setVisible( false ); +} + +void LLFloaterWebContent::onEnterAddress() +{ + // make sure there is at least something there. + // (perhaps this test should be for minimum length of a URL) + std::string url = mAddressCombo->getValue().asString(); + if ( url.length() > 0 ) + { + mWebBrowser->navigateTo( url, "text/html"); + }; +} + +void LLFloaterWebContent::onPopExternal() +{ + // make sure there is at least something there. + // (perhaps this test should be for minimum length of a URL) + std::string url = mAddressCombo->getValue().asString(); + if ( url.length() > 0 ) + { + LLWeb::loadURLExternal( url ); + }; +} diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h index 001d822ada..ecc7e970d8 100644 --- a/indra/newview/llfloaterwebcontent.h +++ b/indra/newview/llfloaterwebcontent.h @@ -1,82 +1,82 @@ -/** - * @file llfloaterwebcontent.h - * @brief floater for displaying web content - e.g. profiles and search (eventually) - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLFLOATERWEBCONTENT_H -#define LL_LLFLOATERWEBCONTENT_H - -#include "llfloater.h" -#include "llmediactrl.h" - -class LLMediaCtrl; -class LLComboBox; -class LLTextBox; -class LLProgressBar; -class LLIconCtrl; - -class LLFloaterWebContent : - public LLFloater, - public LLViewerMediaObserver -{ -public: - LOG_CLASS(LLFloaterWebContent); - LLFloaterWebContent(const LLSD& key); - +/** + * @file llfloaterwebcontent.h + * @brief floater for displaying web content - e.g. profiles and search (eventually) + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERWEBCONTENT_H +#define LL_LLFLOATERWEBCONTENT_H + +#include "llfloater.h" +#include "llmediactrl.h" + +class LLMediaCtrl; +class LLComboBox; +class LLTextBox; +class LLProgressBar; +class LLIconCtrl; + +class LLFloaterWebContent : + public LLFloater, + public LLViewerMediaObserver +{ +public: + LOG_CLASS(LLFloaterWebContent); + LLFloaterWebContent(const LLSD& key); + void initializeURLHistory(); - - static void create(const std::string &url, const std::string& target, const std::string& uuid = LLStringUtil::null); - - static void closeRequest(const std::string &uuid); - static void geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height); - void geometryChanged(S32 x, S32 y, S32 width, S32 height); - - /* virtual */ BOOL postBuild(); - /* virtual */ void onClose(bool app_quitting); - /* virtual */ void draw(); - - // inherited from LLViewerMediaObserver - /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); - - void onClickBack(); - void onClickForward(); - void onClickReload(); - void onClickStop(); - void onEnterAddress(); - void onPopExternal(); - -private: - void open_media(const std::string& media_url, const std::string& target); - void set_current_url(const std::string& url); - - LLMediaCtrl* mWebBrowser; - LLComboBox* mAddressCombo; - LLIconCtrl *mSecureLockIcon; - LLTextBox* mStatusBarText; - LLProgressBar* mStatusBarProgress; - std::string mCurrentURL; - std::string mUUID; -}; - -#endif // LL_LLFLOATERWEBCONTENT_H + + static void create(const std::string &url, const std::string& target, const std::string& uuid = LLStringUtil::null); + + static void closeRequest(const std::string &uuid); + static void geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height); + void geometryChanged(S32 x, S32 y, S32 width, S32 height); + + /* virtual */ BOOL postBuild(); + /* virtual */ void onClose(bool app_quitting); + /* virtual */ void draw(); + + // inherited from LLViewerMediaObserver + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + + void onClickBack(); + void onClickForward(); + void onClickReload(); + void onClickStop(); + void onEnterAddress(); + void onPopExternal(); + +private: + void open_media(const std::string& media_url, const std::string& target); + void set_current_url(const std::string& url); + + LLMediaCtrl* mWebBrowser; + LLComboBox* mAddressCombo; + LLIconCtrl *mSecureLockIcon; + LLTextBox* mStatusBarText; + LLProgressBar* mStatusBarProgress; + std::string mCurrentURL; + std::string mUUID; +}; + +#endif // LL_LLFLOATERWEBCONTENT_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index ce305dcd89..afd565bb26 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -280,19 +280,19 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& void LLIMModel::LLIMSession::onAdHocNameCache(const LLAvatarName& av_name) { if (av_name.mIsDummy) - { - S32 separator_index = mName.rfind(" "); - std::string name = mName.substr(0, separator_index); - ++separator_index; - std::string conference_word = mName.substr(separator_index, mName.length()); - - // additional check that session name is what we expected - if ("Conference" == conference_word) - { - LLStringUtil::format_map_t args; - args["[AGENT_NAME]"] = name; - LLTrans::findString(mName, "conference-title-incoming", args); - } + { + S32 separator_index = mName.rfind(" "); + std::string name = mName.substr(0, separator_index); + ++separator_index; + std::string conference_word = mName.substr(separator_index, mName.length()); + + // additional check that session name is what we expected + if ("Conference" == conference_word) + { + LLStringUtil::format_map_t args; + args["[AGENT_NAME]"] = name; + LLTrans::findString(mName, "conference-title-incoming", args); + } } else { diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index e765a8da2f..a15776c207 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -100,7 +100,7 @@ public: void onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name); - void onAdHocNameCache(const LLAvatarName& av_name); + void onAdHocNameCache(const LLAvatarName& av_name); //*TODO make private static std::string generateHash(const std::set& sorted_uuids); diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 0121bbb1ed..9adf374c71 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -89,15 +89,15 @@ const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+ */ const static boost::regex NAME_AND_TEXT("([^:]+[:]{1})?(\\s*)(.*)"); -/** - * These are recognizers for matching the names of ad-hoc conferences when generating the log file name - * On invited side, an ad-hoc is named like " Conference 2010/11/19 03:43 f0f4" - * On initiating side, an ad-hoc is named like Ad-hoc Conference hash" - * If the naming system for ad-hoc conferences are change in LLIMModel::LLIMSession::buildHistoryFileName() - * then these definition need to be adjusted as well. - */ -const static boost::regex INBOUND_CONFERENCE("^[a-zA-Z]{1,31} [a-zA-Z]{1,31} Conference [0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2} [0-9a-f]{4}"); -const static boost::regex OUTBOUND_CONFERENCE("^Ad-hoc Conference hash[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"); +/** + * These are recognizers for matching the names of ad-hoc conferences when generating the log file name + * On invited side, an ad-hoc is named like " Conference 2010/11/19 03:43 f0f4" + * On initiating side, an ad-hoc is named like Ad-hoc Conference hash" + * If the naming system for ad-hoc conferences are change in LLIMModel::LLIMSession::buildHistoryFileName() + * then these definition need to be adjusted as well. + */ +const static boost::regex INBOUND_CONFERENCE("^[a-zA-Z]{1,31} [a-zA-Z]{1,31} Conference [0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2} [0-9a-f]{4}"); +const static boost::regex OUTBOUND_CONFERENCE("^Ad-hoc Conference hash[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"); //is used to parse complex object names like "Xstreet SL Terminal v2.2.5 st" const static std::string NAME_TEXT_DIVIDER(": "); diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp index a6c1f69c82..dae22521bb 100644 --- a/indra/newview/tests/llremoteparcelrequest_test.cpp +++ b/indra/newview/tests/llremoteparcelrequest_test.cpp @@ -1,134 +1,134 @@ -/** - * @file llremoteparcelrequest_test.cpp - * @author Brad Kittenbrink - * - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "../test/lltut.h" - -#include "../llremoteparcelrequest.h" - -#include "../llagent.h" -#include "message.h" - -namespace { - LLControlGroup s_saved_settings("dummy_settings"); - const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111"); -} - -LLCurl::Responder::Responder() { } -LLCurl::Responder::~Responder() { } -void LLCurl::Responder::error(U32,std::string const &) { } -void LLCurl::Responder::result(LLSD const &) { } -void LLCurl::Responder::errorWithContent(U32 status,std::string const &,LLSD const &) { } -void LLCurl::Responder::completedRaw(U32 status, std::string const &, LLChannelDescriptors const &,boost::shared_ptr const &) { } -void LLCurl::Responder::completed(U32 status, std::string const &, LLSD const &) { } -void LLCurl::Responder::completedHeader(U32 status, std::string const &, LLSD const &) { } -void LLMessageSystem::getF32(char const *,char const *,F32 &,S32) { } -void LLMessageSystem::getU8(char const *,char const *,U8 &,S32) { } -void LLMessageSystem::getS32(char const *,char const *,S32 &,S32) { } -void LLMessageSystem::getString(char const *,char const *, std::string &,S32) { } -void LLMessageSystem::getUUID(char const *,char const *, LLUUID & out_id,S32) -{ - out_id = TEST_PARCEL_ID; -} -void LLMessageSystem::nextBlock(char const *) { } -void LLMessageSystem::addUUID(char const *,LLUUID const &) { } -void LLMessageSystem::addUUIDFast(char const *,LLUUID const &) { } -void LLMessageSystem::nextBlockFast(char const *) { } -void LLMessageSystem::newMessage(char const *) { } -LLMessageSystem * gMessageSystem; -char * _PREHASH_AgentID; -char * _PREHASH_AgentData; -LLAgent gAgent; -LLAgent::LLAgent() : mAgentAccess(s_saved_settings) { } -LLAgent::~LLAgent() { } -void LLAgent::sendReliableMessage(void) { } -LLUUID gAgentSessionID; -LLUUID gAgentID; -LLUIColor::LLUIColor(void) { } -LLAgentAccess::LLAgentAccess(LLControlGroup & settings) : mSavedSettings(settings) { } -LLControlGroup::LLControlGroup(std::string const & name) : LLInstanceTracker(name) { } -LLControlGroup::~LLControlGroup(void) { } - -namespace tut -{ - struct TestObserver : public LLRemoteParcelInfoObserver { - TestObserver() : mProcessed(false) { } - - virtual void processParcelInfo(const LLParcelData& parcel_data) - { - mProcessed = true; - } - - virtual void setParcelID(const LLUUID& parcel_id) { } - - virtual void setErrorStatus(U32 status, const std::string& reason) { } - - bool mProcessed; - }; - - struct RemoteParcelRequestData - { - RemoteParcelRequestData() - { - } - }; - - typedef test_group remoteparcelrequest_t; - typedef remoteparcelrequest_t::object remoteparcelrequest_object_t; - tut::remoteparcelrequest_t tut_remoteparcelrequest("LLRemoteParcelRequest"); - - template<> template<> - void remoteparcelrequest_object_t::test<1>() - { - set_test_name("observer pointer"); - - boost::scoped_ptr observer(new TestObserver()); - - LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance(); - processor.addObserver(LLUUID(TEST_PARCEL_ID), observer.get()); - - processor.processParcelInfoReply(gMessageSystem, NULL); - - ensure(observer->mProcessed); - } - - template<> template<> - void remoteparcelrequest_object_t::test<2>() - { - set_test_name("CHOP-220: dangling observer pointer"); - - LLRemoteParcelInfoObserver * observer = new TestObserver(); - - LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance(); - processor.addObserver(LLUUID(TEST_PARCEL_ID), observer); - - delete observer; - observer = NULL; - - processor.processParcelInfoReply(gMessageSystem, NULL); - } -} +/** + * @file llremoteparcelrequest_test.cpp + * @author Brad Kittenbrink + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "../test/lltut.h" + +#include "../llremoteparcelrequest.h" + +#include "../llagent.h" +#include "message.h" + +namespace { + LLControlGroup s_saved_settings("dummy_settings"); + const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111"); +} + +LLCurl::Responder::Responder() { } +LLCurl::Responder::~Responder() { } +void LLCurl::Responder::error(U32,std::string const &) { } +void LLCurl::Responder::result(LLSD const &) { } +void LLCurl::Responder::errorWithContent(U32 status,std::string const &,LLSD const &) { } +void LLCurl::Responder::completedRaw(U32 status, std::string const &, LLChannelDescriptors const &,boost::shared_ptr const &) { } +void LLCurl::Responder::completed(U32 status, std::string const &, LLSD const &) { } +void LLCurl::Responder::completedHeader(U32 status, std::string const &, LLSD const &) { } +void LLMessageSystem::getF32(char const *,char const *,F32 &,S32) { } +void LLMessageSystem::getU8(char const *,char const *,U8 &,S32) { } +void LLMessageSystem::getS32(char const *,char const *,S32 &,S32) { } +void LLMessageSystem::getString(char const *,char const *, std::string &,S32) { } +void LLMessageSystem::getUUID(char const *,char const *, LLUUID & out_id,S32) +{ + out_id = TEST_PARCEL_ID; +} +void LLMessageSystem::nextBlock(char const *) { } +void LLMessageSystem::addUUID(char const *,LLUUID const &) { } +void LLMessageSystem::addUUIDFast(char const *,LLUUID const &) { } +void LLMessageSystem::nextBlockFast(char const *) { } +void LLMessageSystem::newMessage(char const *) { } +LLMessageSystem * gMessageSystem; +char * _PREHASH_AgentID; +char * _PREHASH_AgentData; +LLAgent gAgent; +LLAgent::LLAgent() : mAgentAccess(s_saved_settings) { } +LLAgent::~LLAgent() { } +void LLAgent::sendReliableMessage(void) { } +LLUUID gAgentSessionID; +LLUUID gAgentID; +LLUIColor::LLUIColor(void) { } +LLAgentAccess::LLAgentAccess(LLControlGroup & settings) : mSavedSettings(settings) { } +LLControlGroup::LLControlGroup(std::string const & name) : LLInstanceTracker(name) { } +LLControlGroup::~LLControlGroup(void) { } + +namespace tut +{ + struct TestObserver : public LLRemoteParcelInfoObserver { + TestObserver() : mProcessed(false) { } + + virtual void processParcelInfo(const LLParcelData& parcel_data) + { + mProcessed = true; + } + + virtual void setParcelID(const LLUUID& parcel_id) { } + + virtual void setErrorStatus(U32 status, const std::string& reason) { } + + bool mProcessed; + }; + + struct RemoteParcelRequestData + { + RemoteParcelRequestData() + { + } + }; + + typedef test_group remoteparcelrequest_t; + typedef remoteparcelrequest_t::object remoteparcelrequest_object_t; + tut::remoteparcelrequest_t tut_remoteparcelrequest("LLRemoteParcelRequest"); + + template<> template<> + void remoteparcelrequest_object_t::test<1>() + { + set_test_name("observer pointer"); + + boost::scoped_ptr observer(new TestObserver()); + + LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance(); + processor.addObserver(LLUUID(TEST_PARCEL_ID), observer.get()); + + processor.processParcelInfoReply(gMessageSystem, NULL); + + ensure(observer->mProcessed); + } + + template<> template<> + void remoteparcelrequest_object_t::test<2>() + { + set_test_name("CHOP-220: dangling observer pointer"); + + LLRemoteParcelInfoObserver * observer = new TestObserver(); + + LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance(); + processor.addObserver(LLUUID(TEST_PARCEL_ID), observer); + + delete observer; + observer = NULL; + + processor.processParcelInfoReply(gMessageSystem, NULL); + } +} diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp index 5f8cd28f29..88ab5a2284 100644 --- a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp +++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp @@ -1,200 +1,200 @@ -/** - * @file llupdaterservice_test.cpp - * @brief Tests of llupdaterservice.cpp. - * - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -// Precompiled header -#include "linden_common.h" -// associated header -#include "../llupdaterservice.h" -#include "../llupdatechecker.h" -#include "../llupdatedownloader.h" -#include "../llupdateinstaller.h" - -#include "../../../test/lltut.h" -//#define DEBUG_ON -#include "../../../test/debug.h" - -#include "llevents.h" -#include "lldir.h" - -/***************************************************************************** -* MOCK'd -*****************************************************************************/ -LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client) -{} -void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl, - std::string const & servicePath, std::string channel, std::string version) -{} -LLUpdateDownloader::LLUpdateDownloader(Client & ) {} -void LLUpdateDownloader::download(LLURI const & , std::string const &, std::string const &, bool){} - -class LLDir_Mock : public LLDir -{ - void initAppDirs(const std::string &app_name, - const std::string& app_read_only_data_dir = "") {} - U32 countFilesInDir(const std::string &dirname, const std::string &mask) - { - return 0; - } - - BOOL getNextFileInDir(const std::string &dirname, - const std::string &mask, - std::string &fname) - { - return false; - } - void getRandomFileInDir(const std::string &dirname, - const std::string &mask, - std::string &fname) {} - std::string getCurPath() { return ""; } - BOOL fileExists(const std::string &filename) const { return false; } - std::string getLLPluginLauncher() { return ""; } - std::string getLLPluginFilename(std::string base_name) { return ""; } - -} gDirUtil; -LLDir* gDirUtilp = &gDirUtil; -LLDir::LLDir() {} -LLDir::~LLDir() {} -S32 LLDir::deleteFilesInDir(const std::string &dirname, - const std::string &mask) -{ return 0; } - -void LLDir::setChatLogsDir(const std::string &path){} -void LLDir::setPerAccountChatLogsDir(const std::string &username){} -void LLDir::setLindenUserDir(const std::string &username){} -void LLDir::setSkinFolder(const std::string &skin_folder){} -bool LLDir::setCacheDir(const std::string &path){ return true; } -void LLDir::dumpCurrentDirectories() {} - -std::string LLDir::getExpandedFilename(ELLPath location, - const std::string &filename) const -{ - return ""; -} - -std::string LLUpdateDownloader::downloadMarkerPath(void) -{ - return ""; -} - -void LLUpdateDownloader::resume(void) {} -void LLUpdateDownloader::cancel(void) {} -void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) {} - -int ll_install_update(std::string const &, std::string const &, bool, LLInstallScriptMode) -{ - return 0; -} - -std::string const & ll_install_failed_marker_path() -{ - static std::string wubba; - return wubba; -} - -/* -#pragma warning(disable: 4273) -llus_mock_llifstream::llus_mock_llifstream(const std::string& _Filename, - ios_base::openmode _Mode, - int _Prot) : - std::basic_istream >(NULL,true) -{} - -llus_mock_llifstream::~llus_mock_llifstream() {} -bool llus_mock_llifstream::is_open() const {return true;} -void llus_mock_llifstream::close() {} -*/ - -/***************************************************************************** -* TUT -*****************************************************************************/ -namespace tut -{ - struct llupdaterservice_data - { - llupdaterservice_data() : - pumps(LLEventPumps::instance()), - test_url("dummy_url"), - test_channel("dummy_channel"), - test_version("dummy_version") - {} - LLEventPumps& pumps; - std::string test_url; - std::string test_channel; - std::string test_version; - }; - - typedef test_group llupdaterservice_group; - typedef llupdaterservice_group::object llupdaterservice_object; - llupdaterservice_group llupdaterservicegrp("LLUpdaterService"); - - template<> template<> - void llupdaterservice_object::test<1>() - { - DEBUG; - LLUpdaterService updater; - bool got_usage_error = false; - try - { - updater.startChecking(); - } - catch(LLUpdaterService::UsageError) - { - got_usage_error = true; - } - ensure("Caught start before params", got_usage_error); - } - - template<> template<> - void llupdaterservice_object::test<2>() - { - DEBUG; - LLUpdaterService updater; - bool got_usage_error = false; - try - { - updater.initialize("1.0",test_url, "update" ,test_channel, test_version); - updater.startChecking(); - updater.initialize("1.0", "other_url", "update", test_channel, test_version); - } - catch(LLUpdaterService::UsageError) - { - got_usage_error = true; - } - ensure("Caught params while running", got_usage_error); - } - - template<> template<> - void llupdaterservice_object::test<3>() - { - DEBUG; - LLUpdaterService updater; - updater.initialize("1.0", test_url, "update", test_channel, test_version); - updater.startChecking(); - ensure(updater.isChecking()); - updater.stopChecking(); - ensure(!updater.isChecking()); - } -} +/** + * @file llupdaterservice_test.cpp + * @brief Tests of llupdaterservice.cpp. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "../llupdaterservice.h" +#include "../llupdatechecker.h" +#include "../llupdatedownloader.h" +#include "../llupdateinstaller.h" + +#include "../../../test/lltut.h" +//#define DEBUG_ON +#include "../../../test/debug.h" + +#include "llevents.h" +#include "lldir.h" + +/***************************************************************************** +* MOCK'd +*****************************************************************************/ +LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client) +{} +void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl, + std::string const & servicePath, std::string channel, std::string version) +{} +LLUpdateDownloader::LLUpdateDownloader(Client & ) {} +void LLUpdateDownloader::download(LLURI const & , std::string const &, std::string const &, bool){} + +class LLDir_Mock : public LLDir +{ + void initAppDirs(const std::string &app_name, + const std::string& app_read_only_data_dir = "") {} + U32 countFilesInDir(const std::string &dirname, const std::string &mask) + { + return 0; + } + + BOOL getNextFileInDir(const std::string &dirname, + const std::string &mask, + std::string &fname) + { + return false; + } + void getRandomFileInDir(const std::string &dirname, + const std::string &mask, + std::string &fname) {} + std::string getCurPath() { return ""; } + BOOL fileExists(const std::string &filename) const { return false; } + std::string getLLPluginLauncher() { return ""; } + std::string getLLPluginFilename(std::string base_name) { return ""; } + +} gDirUtil; +LLDir* gDirUtilp = &gDirUtil; +LLDir::LLDir() {} +LLDir::~LLDir() {} +S32 LLDir::deleteFilesInDir(const std::string &dirname, + const std::string &mask) +{ return 0; } + +void LLDir::setChatLogsDir(const std::string &path){} +void LLDir::setPerAccountChatLogsDir(const std::string &username){} +void LLDir::setLindenUserDir(const std::string &username){} +void LLDir::setSkinFolder(const std::string &skin_folder){} +bool LLDir::setCacheDir(const std::string &path){ return true; } +void LLDir::dumpCurrentDirectories() {} + +std::string LLDir::getExpandedFilename(ELLPath location, + const std::string &filename) const +{ + return ""; +} + +std::string LLUpdateDownloader::downloadMarkerPath(void) +{ + return ""; +} + +void LLUpdateDownloader::resume(void) {} +void LLUpdateDownloader::cancel(void) {} +void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) {} + +int ll_install_update(std::string const &, std::string const &, bool, LLInstallScriptMode) +{ + return 0; +} + +std::string const & ll_install_failed_marker_path() +{ + static std::string wubba; + return wubba; +} + +/* +#pragma warning(disable: 4273) +llus_mock_llifstream::llus_mock_llifstream(const std::string& _Filename, + ios_base::openmode _Mode, + int _Prot) : + std::basic_istream >(NULL,true) +{} + +llus_mock_llifstream::~llus_mock_llifstream() {} +bool llus_mock_llifstream::is_open() const {return true;} +void llus_mock_llifstream::close() {} +*/ + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct llupdaterservice_data + { + llupdaterservice_data() : + pumps(LLEventPumps::instance()), + test_url("dummy_url"), + test_channel("dummy_channel"), + test_version("dummy_version") + {} + LLEventPumps& pumps; + std::string test_url; + std::string test_channel; + std::string test_version; + }; + + typedef test_group llupdaterservice_group; + typedef llupdaterservice_group::object llupdaterservice_object; + llupdaterservice_group llupdaterservicegrp("LLUpdaterService"); + + template<> template<> + void llupdaterservice_object::test<1>() + { + DEBUG; + LLUpdaterService updater; + bool got_usage_error = false; + try + { + updater.startChecking(); + } + catch(LLUpdaterService::UsageError) + { + got_usage_error = true; + } + ensure("Caught start before params", got_usage_error); + } + + template<> template<> + void llupdaterservice_object::test<2>() + { + DEBUG; + LLUpdaterService updater; + bool got_usage_error = false; + try + { + updater.initialize("1.0",test_url, "update" ,test_channel, test_version); + updater.startChecking(); + updater.initialize("1.0", "other_url", "update", test_channel, test_version); + } + catch(LLUpdaterService::UsageError) + { + got_usage_error = true; + } + ensure("Caught params while running", got_usage_error); + } + + template<> template<> + void llupdaterservice_object::test<3>() + { + DEBUG; + LLUpdaterService updater; + updater.initialize("1.0", test_url, "update", test_channel, test_version); + updater.startChecking(); + ensure(updater.isChecking()); + updater.stopChecking(); + ensure(!updater.isChecking()); + } +} -- cgit v1.2.3 From f243f7e550b9dadd26d119bfeb4b215aa809997c Mon Sep 17 00:00:00 2001 From: leyla_linden Date: Thu, 6 Jan 2011 16:42:18 -0800 Subject: added callback for updating wizard dimensions and costs from the model preview properly enabling/disabling wizard buttons xml ui fixes --- indra/newview/llfloatermodelpreview.cpp | 8537 ++++++++++---------- indra/newview/llfloatermodelpreview.h | 16 +- indra/newview/llfloatermodelwizard.cpp | 52 + indra/newview/llfloatermodelwizard.h | 4 +- .../skins/default/xui/en/floater_model_wizard.xml | 200 +- 5 files changed, 4494 insertions(+), 4315 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index a1c2265f62..46c7cba53a 100755 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1,4264 +1,4273 @@ -/** - * @file llfloatermodelpreview.cpp - * @brief LLFloaterModelPreview class implementation - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "dae.h" -//#include "dom.h" -#include "dom/domAsset.h" -#include "dom/domBind_material.h" -#include "dom/domCOLLADA.h" -#include "dom/domConstants.h" -#include "dom/domController.h" -#include "dom/domEffect.h" -#include "dom/domGeometry.h" -#include "dom/domInstance_geometry.h" -#include "dom/domInstance_material.h" -#include "dom/domInstance_node.h" -#include "dom/domInstance_effect.h" -#include "dom/domMaterial.h" -#include "dom/domMatrix.h" -#include "dom/domNode.h" -#include "dom/domProfile_COMMON.h" -#include "dom/domRotate.h" -#include "dom/domScale.h" -#include "dom/domTranslate.h" -#include "dom/domVisual_scene.h" - -#include "llfloatermodelpreview.h" - -#include "llfilepicker.h" -#include "llimagebmp.h" -#include "llimagetga.h" -#include "llimagejpeg.h" -#include "llimagepng.h" - -#include "llagent.h" -#include "llbutton.h" -#include "llcombobox.h" -#include "lldatapacker.h" -#include "lldrawable.h" -#include "lldrawpoolavatar.h" -#include "llrender.h" -#include "llface.h" -#include "lleconomy.h" -#include "llfocusmgr.h" -#include "llfloaterperms.h" -#include "lliconctrl.h" -#include "llmatrix4a.h" -#include "llmenubutton.h" -#include "llmeshrepository.h" -#include "llsdutil_math.h" -#include "lltextbox.h" -#include "lltoolmgr.h" -#include "llui.h" -#include "llvector4a.h" -#include "llviewercamera.h" -#include "llviewerwindow.h" -#include "llvoavatar.h" -#include "llvoavatarself.h" -#include "pipeline.h" -#include "lluictrlfactory.h" -#include "llviewermenu.h" -#include "llviewermenufile.h" -#include "llviewerregion.h" -#include "llviewertexturelist.h" -#include "llstring.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llradiogroup.h" -#include "llsliderctrl.h" -#include "llspinctrl.h" -#include "lltoggleablemenu.h" -#include "llvfile.h" -#include "llvfs.h" -#include "llcallbacklist.h" - -#include "glod/glod.h" - -//static -S32 LLFloaterModelPreview::sUploadAmount = 10; -LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL; - -const S32 PREVIEW_BORDER_WIDTH = 2; -const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; -const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; -const S32 PREF_BUTTON_HEIGHT = 16 + 7 + 16; -const S32 PREVIEW_TEXTURE_HEIGHT = 300; - -void drawBoxOutline(const LLVector3& pos, const LLVector3& size); - - -std::string lod_name[NUM_LOD+1] = -{ - "lowest", - "low", - "medium", - "high", - "I went off the end of the lod_name array. Me so smart." -}; - -std::string lod_triangles_name[NUM_LOD+1] = -{ - "lowest_triangles", - "low_triangles", - "medium_triangles", - "high_triangles", - "I went off the end of the lod_triangles_name array. Me so smart." -}; - -std::string lod_vertices_name[NUM_LOD+1] = -{ - "lowest_vertices", - "low_vertices", - "medium_vertices", - "high_vertices", - "I went off the end of the lod_vertices_name array. Me so smart." -}; - -std::string lod_status_name[NUM_LOD+1] = -{ - "lowest_status", - "low_status", - "medium_status", - "high_status", - "I went off the end of the lod_status_name array. Me so smart." -}; - -std::string lod_icon_name[NUM_LOD+1] = -{ - "status_icon_lowest", - "status_icon_low", - "status_icon_medium", - "status_icon_high", - "I went off the end of the lod_status_name array. Me so smart." -}; - -std::string lod_status_image[NUM_LOD+1] = -{ - "ModelImport_Status_Good", - "ModelImport_Status_Warning", - "ModelImport_Status_Error", - "I went off the end of the lod_status_image array. Me so smart." -}; - -std::string lod_label_name[NUM_LOD+1] = -{ - "lowest_label", - "low_label", - "medium_label", - "high_label", - "I went off the end of the lod_label_name array. Me so smart." -}; - - -bool validate_face(const LLVolumeFace& face) -{ - for (U32 i = 0; i < face.mNumIndices; ++i) - { - if (face.mIndices[i] >= face.mNumVertices) - { - llwarns << "Face has invalid index." << llendl; - return false; - } - } - - return true; -} - -bool validate_model(const LLModel* mdl) -{ - if (mdl->getNumVolumeFaces() == 0) - { - llwarns << "Model has no faces!" << llendl; - return false; - } - - for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) - { - if (mdl->getVolumeFace(i).mNumVertices == 0) - { - llwarns << "Face has no vertices." << llendl; - return false; - } - - if (mdl->getVolumeFace(i).mNumIndices == 0) - { - llwarns << "Face has no indices." << llendl; - return false; - } - - if (!validate_face(mdl->getVolumeFace(i))) - { - return false; - } - } - - return true; -} - -BOOL stop_gloderror() -{ - GLuint error = glodGetError(); - - if (error != GLOD_NO_ERROR) - { - llwarns << "GLOD error detected, cannot generate LOD: " << std::hex << error << llendl; - return TRUE; - } - - return FALSE; -} - - -LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod) - : LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA) - { - mMP = mp; - mLOD = lod; - } - -void LLMeshFilePicker::notify(const std::string& filename) -{ - mMP->loadModel(mFile, mLOD); -} - - -//----------------------------------------------------------------------------- -// LLFloaterModelPreview() -//----------------------------------------------------------------------------- -LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) : -LLFloater(key) -{ - sInstance = this; - mLastMouseX = 0; - mLastMouseY = 0; - mGLName = 0; - mStatusLock = new LLMutex(NULL); - - mLODMode[LLModel::LOD_HIGH] = 0; - for (U32 i = 0; i < LLModel::LOD_HIGH; i++) - { - mLODMode[i] = 1; - } -} - -//----------------------------------------------------------------------------- -// postBuild() -//----------------------------------------------------------------------------- -BOOL LLFloaterModelPreview::postBuild() -{ - if (!LLFloater::postBuild()) - { - return FALSE; - } - - - - - - - childSetAction("lod_browse", onBrowseLOD, this); - - childSetCommitCallback("crease_angle", onGenerateNormalsCommit, this); - childSetCommitCallback("generate_normals", onGenerateNormalsCommit, this); - - childSetCommitCallback("lod_generate", onAutoFillCommit, this); - - childSetCommitCallback("lod_mode", onLODParamCommit, this); - childSetCommitCallback("lod_error_threshold", onLODParamCommit, this); - childSetCommitCallback("lod_triangle_limit", onLODParamCommit, this); - childSetCommitCallback("build_operator", onLODParamCommit, this); - childSetCommitCallback("queue_mode", onLODParamCommit, this); - childSetCommitCallback("border_mode", onLODParamCommit, this); - childSetCommitCallback("share_tolerance", onLODParamCommit, this); - - childSetTextArg("status", "[STATUS]", getString("status_idle")); - - //childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount)); - childSetAction("ok_btn", onUpload, this); - childDisable("ok_btn"); - - childSetAction("clear_materials", onClearMaterials, this); - - childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this); - - childSetCommitCallback("upload_skin", onUploadSkinCommit, this); - childSetCommitCallback("upload_joints", onUploadJointsCommit, this); - - childSetCommitCallback("import_scale", onImportScaleCommit, this); - - childSetCommitCallback("lod_file_or_limit", refresh, this); - childSetCommitCallback("physics_load_radio", refresh, this); - //childSetCommitCallback("physics_optimize", refresh, this); - //childSetCommitCallback("physics_use_hull", refresh, this); - - childDisable("upload_skin"); - childDisable("upload_joints"); - childDisable("ok_btn"); - - mViewOptionMenuButton = getChild("options_gear_btn"); - - mCommitCallbackRegistrar.add("ModelImport.ViewOption.Action", boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _2)); - mEnableCallbackRegistrar.add("ModelImport.ViewOption.Check", boost::bind(&LLFloaterModelPreview::isViewOptionChecked, this, _2)); - mEnableCallbackRegistrar.add("ModelImport.ViewOption.Enabled", boost::bind(&LLFloaterModelPreview::isViewOptionEnabled, this, _2)); - - - - mViewOptionMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_model_import_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mViewOptionMenuButton->setMenu(mViewOptionMenu, LLMenuButton::MP_BOTTOM_LEFT); - - initDecompControls(); - - LLView* preview_panel = getChild("preview_panel"); - - mPreviewRect = preview_panel->getRect(); - - mModelPreview = new LLModelPreview(512, 512, this); - mModelPreview->setPreviewTarget(16.f); - - //set callbacks for left click on line editor rows - for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) - { - LLTextBox* text = getChild(lod_label_name[i]); - if (text) - { - text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); - } - - text = getChild(lod_triangles_name[i]); - if (text) - { - text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); - } - - text = getChild(lod_vertices_name[i]); - if (text) - { - text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); - } - - text = getChild(lod_status_name[i]); - if (text) - { - text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); - } - } - - return TRUE; -} - -//----------------------------------------------------------------------------- -// LLFloaterModelPreview() -//----------------------------------------------------------------------------- -LLFloaterModelPreview::~LLFloaterModelPreview() -{ - sInstance = NULL; - - if ( mModelPreview->containsRiggedAsset() ) - { - gAgentAvatarp->resetJointPositions(); - } - - delete mModelPreview; - - if (mGLName) - { - LLImageGL::deleteTextures(1, &mGLName ); - } - - delete mStatusLock; - mStatusLock = NULL; -} - -void LLFloaterModelPreview::onViewOptionChecked(const LLSD& userdata) -{ - if (mModelPreview) - { - mModelPreview->mViewOption[userdata.asString()] = !mModelPreview->mViewOption[userdata.asString()]; - - mModelPreview->refresh(); - } -} - -bool LLFloaterModelPreview::isViewOptionChecked(const LLSD& userdata) -{ - if (mModelPreview) - { - return mModelPreview->mViewOption[userdata.asString()]; - } - - return false; -} - -bool LLFloaterModelPreview::isViewOptionEnabled(const LLSD& userdata) -{ - return !mViewOptionDisabled[userdata.asString()]; -} - -void LLFloaterModelPreview::setViewOptionEnabled(const std::string& option, bool enabled) -{ - mViewOptionDisabled[option] = !enabled; -} - -void LLFloaterModelPreview::enableViewOption(const std::string& option) -{ - setViewOptionEnabled(option, true); -} - -void LLFloaterModelPreview::disableViewOption(const std::string& option) -{ - setViewOptionEnabled(option, false); -} - -void LLFloaterModelPreview::loadModel(S32 lod) -{ - mModelPreview->mLoading = true; - - (new LLMeshFilePicker(mModelPreview, lod))->getFile(); -} - -//static -void LLFloaterModelPreview::onImportScaleCommit(LLUICtrl*,void* userdata) -{ - LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; - - if (!fp->mModelPreview) - { - return; - } - - fp->mModelPreview->calcResourceCost(); - fp->mModelPreview->refresh(); -} - -//static -void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata) -{ - LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; - - if (!fp->mModelPreview) - { - return; - } - - fp->mModelPreview->refresh(); -} - -//static -void LLFloaterModelPreview::onUploadSkinCommit(LLUICtrl*,void* userdata) -{ - LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; - - if (!fp->mModelPreview) - { - return; - } - - fp->mModelPreview->refresh(); - fp->mModelPreview->resetPreviewTarget(); - fp->mModelPreview->clearBuffers(); -} - -//static -void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; - - if (!fp->mModelPreview) - { - return; - } - - S32 which_mode = 0; - - LLComboBox* combo = (LLComboBox*) ctrl; - - which_mode = (NUM_LOD-1)-combo->getFirstSelectedIndex(); // combo box list of lods is in reverse order - - fp->mModelPreview->setPreviewLOD(which_mode); -} - -//static -void LLFloaterModelPreview::onGenerateNormalsCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; - - fp->mModelPreview->generateNormals(); -} - -//static -void LLFloaterModelPreview::onExplodeCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview* fp = LLFloaterModelPreview::sInstance; - - fp->mModelPreview->refresh(); -} - -//static -void LLFloaterModelPreview::onAutoFillCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; - - fp->mModelPreview->genLODs(); -} - -//static -void LLFloaterModelPreview::onLODParamCommit(LLUICtrl* ctrl, void* userdata) -{ - LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; - fp->mModelPreview->genLODs(fp->mModelPreview->mPreviewLOD); - fp->mModelPreview->updateStatusMessages(); - fp->mModelPreview->refresh(); -} - - -//----------------------------------------------------------------------------- -// draw() -//----------------------------------------------------------------------------- -void LLFloaterModelPreview::draw() -{ - LLFloater::draw(); - LLRect r = getRect(); - - mModelPreview->update(); - - if (!mModelPreview->mLoading) - { - childSetTextArg("status", "[STATUS]", getString("status_idle")); - } - - childSetTextArg("prim_cost", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost)); - childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size())); - - if (!mCurRequest.empty()) - { - LLMutexLock lock(mStatusLock); - childSetTextArg("status", "[STATUS]", mStatusMessage); - } - - U32 resource_cost = mModelPreview->mResourceCost*10; - - if (childGetValue("upload_textures").asBoolean()) - { - resource_cost += mModelPreview->mTextureSet.size()*10; - } - - childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", resource_cost)); - - if (mModelPreview) - { - gGL.color3f(1.f, 1.f, 1.f); - - gGL.getTexUnit(0)->bind(mModelPreview); - - - LLView* preview_panel = getChild("preview_panel"); - - LLRect rect = preview_panel->getRect(); - if (rect != mPreviewRect) - { - mModelPreview->refresh(); - mPreviewRect = preview_panel->getRect(); - } - - gGL.begin( LLRender::QUADS ); - { - gGL.texCoord2f(0.f, 1.f); - gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex2i(mPreviewRect.mRight, mPreviewRect.mBottom); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex2i(mPreviewRect.mRight, mPreviewRect.mTop); - } - gGL.end(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } -} - -//----------------------------------------------------------------------------- -// handleMouseDown() -//----------------------------------------------------------------------------- -BOOL LLFloaterModelPreview::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (mPreviewRect.pointInRect(x, y)) - { - bringToFront( x, y ); - gFocusMgr.setMouseCapture(this); - gViewerWindow->hideCursor(); - mLastMouseX = x; - mLastMouseY = y; - return TRUE; - } - - return LLFloater::handleMouseDown(x, y, mask); -} - -//----------------------------------------------------------------------------- -// handleMouseUp() -//----------------------------------------------------------------------------- -BOOL LLFloaterModelPreview::handleMouseUp(S32 x, S32 y, MASK mask) -{ - gFocusMgr.setMouseCapture(FALSE); - gViewerWindow->showCursor(); - return LLFloater::handleMouseUp(x, y, mask); -} - -//----------------------------------------------------------------------------- -// handleHover() -//----------------------------------------------------------------------------- -BOOL LLFloaterModelPreview::handleHover (S32 x, S32 y, MASK mask) -{ - MASK local_mask = mask & ~MASK_ALT; - - if (mModelPreview && hasMouseCapture()) - { - if (local_mask == MASK_PAN) - { - // pan here - mModelPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f); - } - else if (local_mask == MASK_ORBIT) - { - F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; - F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f; - - mModelPreview->rotate(yaw_radians, pitch_radians); - } - else - { - - F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; - F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f; - - mModelPreview->rotate(yaw_radians, 0.f); - mModelPreview->zoom(zoom_amt); - } - - - mModelPreview->refresh(); - - LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY); - } - - if (!mPreviewRect.pointInRect(x, y) || !mModelPreview) - { - return LLFloater::handleHover(x, y, mask); - } - else if (local_mask == MASK_ORBIT) - { - gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA); - } - else if (local_mask == MASK_PAN) - { - gViewerWindow->setCursor(UI_CURSOR_TOOLPAN); - } - else - { - gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN); - } - - return TRUE; -} - -//----------------------------------------------------------------------------- -// handleScrollWheel() -//----------------------------------------------------------------------------- -BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - if (mPreviewRect.pointInRect(x, y) && mModelPreview) - { - mModelPreview->zoom((F32)clicks * -0.2f); - mModelPreview->refresh(); - } - - return TRUE; -} - -//static -void LLFloaterModelPreview::onPhysicsParamCommit(LLUICtrl* ctrl, void* data) -{ - if (LLConvexDecomposition::getInstance() == NULL) - { - llinfos << "convex decomposition tool is a stub on this platform. cannot get decomp." << llendl; - return; - } - - if (sInstance) - { - LLCDParam* param = (LLCDParam*) data; - std::string name(param->mName); - sInstance->mDecompParams[name] = ctrl->getValue(); - - if (name == "Simplify Method") - { - if (ctrl->getValue().asInteger() == 0) - { - sInstance->childSetVisible("Retain%", true); - sInstance->childSetVisible("Detail Scale", false); - } - else - { - sInstance->childSetVisible("Retain%", false); - sInstance->childSetVisible("Detail Scale", true); - } - } - } -} - -//static -void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data) -{ - LLCDStageData* stage = (LLCDStageData*) data; - - if (sInstance) - { - if (!sInstance->mCurRequest.empty()) - { - llinfos << "Decomposition request still pending." << llendl; - return; - } - - if (sInstance->mModelPreview) - { - for (S32 i = 0; i < sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS].size(); ++i) - { - LLModel* mdl = sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS][i]; - DecompRequest* request = new DecompRequest(stage->mName, mdl); - sInstance->mCurRequest.insert(request); - gMeshRepo.mDecompThread->submitRequest(request); - } - } - } -} - -//static -void LLFloaterModelPreview::onPhysicsBrowse(LLUICtrl* ctrl, void* userdata) -{ - sInstance->loadModel(LLModel::LOD_PHYSICS); -} - -//static -void LLFloaterModelPreview::onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata) -{ - S32 which_mode = 3; - LLCtrlSelectionInterface* iface = sInstance->childGetSelectionInterface("physics_lod_combo"); - if (iface) - { - which_mode = iface->getFirstSelectedIndex(); - } - - sInstance->mModelPreview->setPhysicsFromLOD(which_mode); -} - -//static -void LLFloaterModelPreview::onPhysicsStageCancel(LLUICtrl* ctrl, void*data) -{ - if (sInstance) - { - for (std::set >::iterator iter = sInstance->mCurRequest.begin(); - iter != sInstance->mCurRequest.end(); ++iter) - { - DecompRequest* req = *iter; - req->mContinue = 0; - } - } -} - -void LLFloaterModelPreview::initDecompControls() -{ - LLSD key; - - childSetCommitCallback("cancel_btn", onPhysicsStageCancel, NULL); - childSetCommitCallback("physics_lod_combo", onPhysicsUseLOD, NULL); - childSetCommitCallback("physics_browse", onPhysicsBrowse, NULL); - - static const LLCDStageData* stage = NULL; - static S32 stage_count = 0; - - if (!stage && LLConvexDecomposition::getInstance() != NULL) - { - stage_count = LLConvexDecomposition::getInstance()->getStages(&stage); - } - - static const LLCDParam* param = NULL; - static S32 param_count = 0; - if (!param && LLConvexDecomposition::getInstance() != NULL) - { - param_count = LLConvexDecomposition::getInstance()->getParameters(¶m); - } - - for (S32 j = stage_count-1; j >= 0; --j) - { - LLButton* button = getChild(stage[j].mName); - if (button) - { - button->setCommitCallback(onPhysicsStageExecute, (void*) &stage[j]); - } - - gMeshRepo.mDecompThread->mStageID[stage[j].mName] = j; - // protected against stub by stage_count being 0 for stub above - LLConvexDecomposition::getInstance()->registerCallback(j, LLPhysicsDecomp::llcdCallback); - - //llinfos << "Physics decomp stage " << stage[j].mName << " (" << j << ") parameters:" << llendl; - //llinfos << "------------------------------------" << llendl; - - for (S32 i = 0; i < param_count; ++i) - { - if (param[i].mStage != j) - { - continue; - } - - std::string name(param[i].mName ? param[i].mName : ""); - std::string description(param[i].mDescription ? param[i].mDescription : ""); - - std::string type = "unknown"; - - llinfos << name << " - " << description << llendl; - - if (param[i].mType == LLCDParam::LLCD_FLOAT) - { - mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mFloat); - //llinfos << "Type: float, Default: " << param[i].mDefault.mFloat << llendl; - - LLSliderCtrl* slider = getChild(name); - if (slider) - { - slider->setMinValue(param[i].mDetails.mRange.mLow.mFloat); - slider->setMaxValue(param[i].mDetails.mRange.mHigh.mFloat); - slider->setIncrement(param[i].mDetails.mRange.mDelta.mFloat); - slider->setValue(param[i].mDefault.mFloat); - slider->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); - } - } - else if (param[i].mType == LLCDParam::LLCD_INTEGER) - { - mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue); - //llinfos << "Type: integer, Default: " << param[i].mDefault.mIntOrEnumValue << llendl; - - LLSliderCtrl* slider = getChild(name); - if (slider) - { - slider->setMinValue(param[i].mDetails.mRange.mLow.mIntOrEnumValue); - slider->setMaxValue(param[i].mDetails.mRange.mHigh.mIntOrEnumValue); - slider->setIncrement(param[i].mDetails.mRange.mDelta.mIntOrEnumValue); - slider->setValue(param[i].mDefault.mIntOrEnumValue); - slider->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); - } - } - else if (param[i].mType == LLCDParam::LLCD_BOOLEAN) - { - mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mBool); - //llinfos << "Type: boolean, Default: " << (param[i].mDefault.mBool ? "True" : "False") << llendl; - - LLCheckBoxCtrl* check_box = getChild(name); - if (check_box) - { - check_box->setValue(param[i].mDefault.mBool); - check_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); - } - } - else if (param[i].mType == LLCDParam::LLCD_ENUM) - { - mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue); - //llinfos << "Type: enum, Default: " << param[i].mDefault.mIntOrEnumValue << llendl; - - { //plug into combo box - - //llinfos << "Accepted values: " << llendl; - LLComboBox* combo_box = getChild(name); - for (S32 k = 0; k < param[i].mDetails.mEnumValues.mNumEnums; ++k) - { - //llinfos << param[i].mDetails.mEnumValues.mEnumsArray[k].mValue - // << " - " << param[i].mDetails.mEnumValues.mEnumsArray[k].mName << llendl; - - combo_box->add(param[i].mDetails.mEnumValues.mEnumsArray[k].mName, - LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue)); - } - combo_box->setValue(param[i].mDefault.mIntOrEnumValue); - combo_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); - } - - //llinfos << "----" << llendl; - } - //llinfos << "-----------------------------" << llendl; - } - } - - childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this); -} - -//----------------------------------------------------------------------------- -// onMouseCaptureLost() -//----------------------------------------------------------------------------- -// static -void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handler) -{ - gViewerWindow->showCursor(); -} - -//----------------------------------------------------------------------------- -// LLModelLoader -//----------------------------------------------------------------------------- -LLModelLoader::LLModelLoader(std::string filename, S32 lod, LLModelPreview* preview) -: LLThread("Model Loader"), mFilename(filename), mLod(lod), mPreview(preview), mState(STARTING), mFirstTransform(TRUE) -{ - mJointMap["mPelvis"] = "mPelvis"; - mJointMap["mTorso"] = "mTorso"; - mJointMap["mChest"] = "mChest"; - mJointMap["mNeck"] = "mNeck"; - mJointMap["mHead"] = "mHead"; - mJointMap["mSkull"] = "mSkull"; - mJointMap["mEyeRight"] = "mEyeRight"; - mJointMap["mEyeLeft"] = "mEyeLeft"; - mJointMap["mCollarLeft"] = "mCollarLeft"; - mJointMap["mShoulderLeft"] = "mShoulderLeft"; - mJointMap["mElbowLeft"] = "mElbowLeft"; - mJointMap["mWristLeft"] = "mWristLeft"; - mJointMap["mCollarRight"] = "mCollarRight"; - mJointMap["mShoulderRight"] = "mShoulderRight"; - mJointMap["mElbowRight"] = "mElbowRight"; - mJointMap["mWristRight"] = "mWristRight"; - mJointMap["mHipRight"] = "mHipRight"; - mJointMap["mKneeRight"] = "mKneeRight"; - mJointMap["mAnkleRight"] = "mAnkleRight"; - mJointMap["mFootRight"] = "mFootRight"; - mJointMap["mToeRight"] = "mToeRight"; - mJointMap["mHipLeft"] = "mHipLeft"; - mJointMap["mKneeLeft"] = "mKneeLeft"; - mJointMap["mAnkleLeft"] = "mAnkleLeft"; - mJointMap["mFootLeft"] = "mFootLeft"; - mJointMap["mToeLeft"] = "mToeLeft"; - - mJointMap["avatar_mPelvis"] = "mPelvis"; - mJointMap["avatar_mTorso"] = "mTorso"; - mJointMap["avatar_mChest"] = "mChest"; - mJointMap["avatar_mNeck"] = "mNeck"; - mJointMap["avatar_mHead"] = "mHead"; - mJointMap["avatar_mSkull"] = "mSkull"; - mJointMap["avatar_mEyeRight"] = "mEyeRight"; - mJointMap["avatar_mEyeLeft"] = "mEyeLeft"; - mJointMap["avatar_mCollarLeft"] = "mCollarLeft"; - mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft"; - mJointMap["avatar_mElbowLeft"] = "mElbowLeft"; - mJointMap["avatar_mWristLeft"] = "mWristLeft"; - mJointMap["avatar_mCollarRight"] = "mCollarRight"; - mJointMap["avatar_mShoulderRight"] = "mShoulderRight"; - mJointMap["avatar_mElbowRight"] = "mElbowRight"; - mJointMap["avatar_mWristRight"] = "mWristRight"; - mJointMap["avatar_mHipRight"] = "mHipRight"; - mJointMap["avatar_mKneeRight"] = "mKneeRight"; - mJointMap["avatar_mAnkleRight"] = "mAnkleRight"; - mJointMap["avatar_mFootRight"] = "mFootRight"; - mJointMap["avatar_mToeRight"] = "mToeRight"; - mJointMap["avatar_mHipLeft"] = "mHipLeft"; - mJointMap["avatar_mKneeLeft"] = "mKneeLeft"; - mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft"; - mJointMap["avatar_mFootLeft"] = "mFootLeft"; - mJointMap["avatar_mToeLeft"] = "mToeLeft"; - - - mJointMap["hip"] = "mPelvis"; - mJointMap["abdomen"] = "mTorso"; - mJointMap["chest"] = "mChest"; - mJointMap["neck"] = "mNeck"; - mJointMap["head"] = "mHead"; - mJointMap["figureHair"] = "mSkull"; - mJointMap["lCollar"] = "mCollarLeft"; - mJointMap["lShldr"] = "mShoulderLeft"; - mJointMap["lForeArm"] = "mElbowLeft"; - mJointMap["lHand"] = "mWristLeft"; - mJointMap["rCollar"] = "mCollarRight"; - mJointMap["rShldr"] = "mShoulderRight"; - mJointMap["rForeArm"] = "mElbowRight"; - mJointMap["rHand"] = "mWristRight"; - mJointMap["rThigh"] = "mHipRight"; - mJointMap["rShin"] = "mKneeRight"; - mJointMap["rFoot"] = "mFootRight"; - mJointMap["lThigh"] = "mHipLeft"; - mJointMap["lShin"] = "mKneeLeft"; - mJointMap["lFoot"] = "mFootLeft"; -} - -void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform) -{ - LLVector4a box[] = - { - LLVector4a(-1, 1,-1), - LLVector4a(-1, 1, 1), - LLVector4a(-1,-1,-1), - LLVector4a(-1,-1, 1), - LLVector4a( 1, 1,-1), - LLVector4a( 1, 1, 1), - LLVector4a( 1,-1,-1), - LLVector4a( 1,-1, 1), - }; - - for (S32 j = 0; j < model->getNumVolumeFaces(); ++j) - { - const LLVolumeFace& face = model->getVolumeFace(j); - - LLVector4a center; - center.setAdd(face.mExtents[0], face.mExtents[1]); - center.mul(0.5f); - LLVector4a size; - size.setSub(face.mExtents[1],face.mExtents[0]); - size.mul(0.5f); - - for (U32 i = 0; i < 8; i++) - { - LLVector4a t; - t.setMul(size, box[i]); - t.add(center); - - LLVector4a v; - - mat.affineTransform(t, v); - - if (first_transform) - { - first_transform = FALSE; - min = max = v; - } - else - { - update_min_max(min, max, v); - } - } - } -} - -void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform) -{ - LLVector4a mina, maxa; - LLMatrix4a mata; - - mata.loadu(mat); - mina.load3(min.mV); - maxa.load3(max.mV); - - stretch_extents(model, mata, mina, maxa, first_transform); - - min.set(mina.getF32ptr()); - max.set(maxa.getF32ptr()); -} - -void LLModelLoader::run() -{ - DAE dae; - domCOLLADA* dom = dae.open(mFilename); - - if (dom) - { - daeDatabase* db = dae.getDatabase(); - - daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH); - - daeDocument* doc = dae.getDoc(mFilename); - if (!doc) - { - llwarns << "can't find internal doc" << llendl; - return; - } - - daeElement* root = doc->getDomRoot(); - if (!root) - { - llwarns << "document has no root" << llendl; - return; - } - - //get unit scale - mTransform.setIdentity(); - - domAsset::domUnit* unit = daeSafeCast(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID()))); - - if (unit) - { - F32 meter = unit->getMeter(); - mTransform.mMatrix[0][0] = meter; - mTransform.mMatrix[1][1] = meter; - mTransform.mMatrix[2][2] = meter; - } - - //get up axis rotation - LLMatrix4 rotation; - - domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP - domAsset::domUp_axis* up_axis = - daeSafeCast(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID()))); - - if (up_axis) - { - up = up_axis->getValue(); - } - - if (up == UPAXISTYPE_X_UP) - { - rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f); - } - else if (up == UPAXISTYPE_Y_UP) - { - rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); - } - - rotation *= mTransform; - mTransform = rotation; - - - for (daeInt idx = 0; idx < count; ++idx) - { //build map of domEntities to LLModel - domMesh* mesh = NULL; - db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH); - - if (mesh) - { - LLPointer model = LLModel::loadModelFromDomMesh(mesh); - - if (model.notNull() && validate_model(model)) - { - mModelList.push_back(model); - mModel[mesh] = model; - } - } - } - - count = db->getElementCount(NULL, COLLADA_TYPE_SKIN); - for (daeInt idx = 0; idx < count; ++idx) - { //add skinned meshes as instances - domSkin* skin = NULL; - db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN); - - if (skin) - { - domGeometry* geom = daeSafeCast(skin->getSource().getElement()); - - if (geom) - { - domMesh* mesh = geom->getMesh(); - if (mesh) - { - LLModel* model = mModel[mesh]; - if (model) - { - LLVector3 mesh_scale_vector; - LLVector3 mesh_translation_vector; - model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); - - LLMatrix4 normalized_transformation; - normalized_transformation.setTranslation(mesh_translation_vector); - - LLMatrix4 mesh_scale; - mesh_scale.initScale(mesh_scale_vector); - mesh_scale *= normalized_transformation; - normalized_transformation = mesh_scale; - - glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix); - inv_mat = inv_mat.inverse(); - LLMatrix4 inverse_normalized_transformation(inv_mat.m); - - domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix(); - - if (bind_mat) - { //get bind shape matrix - domFloat4x4& dom_value = bind_mat->getValue(); - - for (int i = 0; i < 4; i++) - { - for(int j = 0; j < 4; j++) - { - model->mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4]; - } - } - - LLMatrix4 trans = normalized_transformation; - trans *= model->mBindShapeMatrix; - model->mBindShapeMatrix = trans; - - } - - - //The joint transfom map that we'll populate below - std::map jointTransforms; - jointTransforms.clear(); - - //Some collada setup for accessing the skeleton - daeElement* pElement = 0; - dae.getDatabase()->getElement( &pElement, 0, 0, "skeleton" ); - - //Try to get at the skeletal instance controller - domInstance_controller::domSkeleton* pSkeleton = daeSafeCast( pElement ); - bool missingSkeletonOrScene = false; - - //If no skeleton, do a breadth-first search to get at specific joints - if ( !pSkeleton ) - { - daeElement* pScene = root->getDescendant("visual_scene"); - if ( !pScene ) - { - llwarns<<"No visual scene - unable to parse bone offsets "< > children = pScene->getChildren(); - S32 childCount = children.getCount(); - - //Process any children that are joints - //Not all children are joints, some code be ambient lights, cameras, geometry etc.. - for (S32 i = 0; i < childCount; ++i) - { - domNode* pNode = daeSafeCast(children[i]); - if ( isNodeAJoint( pNode ) ) - { - processJointNode( pNode, jointTransforms ); - } - } - } - } - else - //Has Skeleton - { - //Get the root node of the skeleton - daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement(); - if ( pSkeletonRootNode ) - { - //Once we have the root node - start acccessing it's joint components - const int jointCnt = mJointMap.size(); - std::map :: const_iterator jointIt = mJointMap.begin(); - - //Loop over all the possible joints within the .dae - using the allowed joint list in the ctor. - for ( int i=0; i( resolver.getElement() ); - if ( pJoint ) - { - //Pull out the translate id and store it in the jointTranslations map - daeSIDResolver jointResolver( pJoint, "./translate" ); - domTranslate* pTranslate = daeSafeCast( jointResolver.getElement() ); - - LLMatrix4 workingTransform; - - //Translation via SID - if ( pTranslate ) - { - extractTranslation( pTranslate, workingTransform ); - } - else - { - //Translation via child from element - daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" ); - if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() ) - { - llwarns<< "The found element is not a translate node" < :: const_iterator jointIt = mJointMap.begin(); - for ( int i=0; igetJoint( lookingForJoint ); - if ( pJoint ) - { - pJoint->storeCurrentXform( jointTransform.getTranslation() ); - } - else - { - //Most likely an error in the asset. - llwarns<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << llendl; - } - //Reposition the avatars pelvis (avPos+offset) - //if ( lookingForJoint == "mPelvis" ) - //{ - // const LLVector3& pos = gAgentAvatarp->getCharacterPosition(); - // gAgentAvatarp->setPelvisOffset( true, jointTransform.getTranslation() ); - // gAgentAvatarp->setPosition( pos + jointTransform.getTranslation() ); - //} - } - } - } //missingSkeletonOrScene - - domSkin::domJoints* joints = skin->getJoints(); - - domInputLocal_Array& joint_input = joints->getInput_array(); - - for (size_t i = 0; i < joint_input.getCount(); ++i) - { - domInputLocal* input = joint_input.get(i); - xsNMTOKEN semantic = input->getSemantic(); - - if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0) - { //found joint source, fill model->mJointMap and model->mJointList - daeElement* elem = input->getSource().getElement(); - - domSource* source = daeSafeCast(elem); - if (source) - { - - - domName_array* names_source = source->getName_array(); - - if (names_source) - { - domListOfNames &names = names_source->getValue(); - - for (size_t j = 0; j < names.getCount(); ++j) - { - std::string name(names.get(j)); - if (mJointMap.find(name) != mJointMap.end()) - { - name = mJointMap[name]; - } - model->mJointList.push_back(name); - model->mJointMap[name] = j; - } - } - else - { - domIDREF_array* names_source = source->getIDREF_array(); - if (names_source) - { - xsIDREFS& names = names_source->getValue(); - - for (size_t j = 0; j < names.getCount(); ++j) - { - std::string name(names.get(j).getID()); - if (mJointMap.find(name) != mJointMap.end()) - { - name = mJointMap[name]; - } - model->mJointList.push_back(name); - model->mJointMap[name] = j; - } - } - } - } - } - else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0) - { //found inv_bind_matrix array, fill model->mInvBindMatrix - domSource* source = daeSafeCast(input->getSource().getElement()); - if (source) - { - domFloat_array* t = source->getFloat_array(); - if (t) - { - domListOfFloats& transform = t->getValue(); - S32 count = transform.getCount()/16; - - for (S32 k = 0; k < count; ++k) - { - LLMatrix4 mat; - - for (int i = 0; i < 4; i++) - { - for(int j = 0; j < 4; j++) - { - mat.mMatrix[i][j] = transform[k*16 + i + j*4]; - } - } - - model->mInvBindMatrix.push_back(mat); - } - } - } - } - } - - //We need to construct the alternate bind matrix (which contains the new joint positions) - //in the same order as they were stored in the joint buffer. The joints associated - //with the skeleton are not stored in the same order as they are in the exported joint buffer. - //This remaps the skeletal joints to be in the same order as the joints stored in the model. - std::vector :: const_iterator jointIt = model->mJointList.begin(); - const int jointCnt = model->mJointList.size(); - for ( int i=0; imInvBindMatrix[i]; - newInverse.setTranslation( jointTransforms[lookingForJoint].getTranslation() ); - model->mAlternateBindMatrix.push_back( newInverse ); - } - else - { - llwarns<<"Possibly misnamed/missing joint [" <getVertices(); - if (verts) - { - domInputLocal_Array& inputs = verts->getInput_array(); - for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i) - { - if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0) - { - domSource* pos_source = daeSafeCast(inputs[i]->getSource().getElement()); - if (pos_source) - { - domFloat_array* pos_array = pos_source->getFloat_array(); - if (pos_array) - { - domListOfFloats& pos = pos_array->getValue(); - - for (size_t j = 0; j < pos.getCount(); j += 3) - { - if (pos.getCount() <= j+2) - { - llerrs << "WTF?" << llendl; - } - - LLVector3 v(pos[j], pos[j+1], pos[j+2]); - - //transform from COLLADA space to volume space - v = v * inverse_normalized_transformation; - - model->mPosition.push_back(v); - } - } - } - } - } - } - - //grab skin weights array - domSkin::domVertex_weights* weights = skin->getVertex_weights(); - if (weights) - { - domInputLocalOffset_Array& inputs = weights->getInput_array(); - domFloat_array* vertex_weights = NULL; - for (size_t i = 0; i < inputs.getCount(); ++i) - { - if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0) - { - domSource* weight_source = daeSafeCast(inputs[i]->getSource().getElement()); - if (weight_source) - { - vertex_weights = weight_source->getFloat_array(); - } - } - } - - if (vertex_weights) - { - domListOfFloats& w = vertex_weights->getValue(); - domListOfUInts& vcount = weights->getVcount()->getValue(); - domListOfInts& v = weights->getV()->getValue(); - - U32 c_idx = 0; - for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx) - { //for each vertex - daeUInt count = vcount[vc_idx]; - - //create list of weights that influence this vertex - LLModel::weight_list weight_list; - - for (daeUInt i = 0; i < count; ++i) - { //for each weight - daeInt joint_idx = v[c_idx++]; - daeInt weight_idx = v[c_idx++]; - - if (joint_idx == -1) - { - //ignore bindings to bind_shape_matrix - continue; - } - - F32 weight_value = w[weight_idx]; - - weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value)); - } - - //sort by joint weight - std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); - - std::vector wght; - - F32 total = 0.f; - - for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i) - { //take up to 4 most significant weights - if (weight_list[i].mWeight > 0.f) - { - wght.push_back( weight_list[i] ); - total += weight_list[i].mWeight; - } - } - - F32 scale = 1.f/total; - if (scale != 1.f) - { //normalize weights - for (U32 i = 0; i < wght.size(); ++i) - { - wght[i].mWeight *= scale; - } - } - - model->mSkinWeights[model->mPosition[vc_idx]] = wght; - } - - //add instance to scene for this model - - LLMatrix4 transformation = mTransform; - // adjust the transformation to compensate for mesh normalization - - LLMatrix4 mesh_translation; - mesh_translation.setTranslation(mesh_translation_vector); - mesh_translation *= transformation; - transformation = mesh_translation; - - LLMatrix4 mesh_scale; - mesh_scale.initScale(mesh_scale_vector); - mesh_scale *= transformation; - transformation = mesh_scale; - - std::vector materials; - materials.resize(model->getNumVolumeFaces()); - mScene[transformation].push_back(LLModelInstance(model, transformation, materials)); - stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); - } - } - } - } - } - } - } - - daeElement* scene = root->getDescendant("visual_scene"); - - if (!scene) - { - llwarns << "document has no visual_scene" << llendl; - setLoadState( ERROR_PARSING ); - return; - } - - processElement(scene); - - doOnIdleOneTime(boost::bind(&LLModelPreview::loadModelCallback,mPreview,mLod)); - } -} - -bool LLModelLoader::isNodeAJoint( domNode* pNode ) -{ - if ( pNode->getName() == NULL) - { - return false; - } - - if ( mJointMap.find( pNode->getName() ) != mJointMap.end() ) - { - return true; - } - - return false; -} - -void LLModelLoader::extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ) -{ - domFloat3 jointTrans = pTranslate->getValue(); - LLVector3 singleJointTranslation( jointTrans[0], jointTrans[1], jointTrans[2] ); - transform.setTranslation( singleJointTranslation ); -} - -void LLModelLoader::extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ) -{ - domTranslate* pTranslateChild = dynamic_cast( pTranslateElement ); - domFloat3 translateChild = pTranslateChild->getValue(); - LLVector3 singleJointTranslation( translateChild[0], translateChild[1], translateChild[2] ); - transform.setTranslation( singleJointTranslation ); -} - -void LLModelLoader::processJointNode( domNode* pNode, std::map& jointTransforms ) -{ - if (pNode->getName() == NULL) - { - llwarns << "nameless node, can't process" << llendl; - return; - } - - //llwarns<<"ProcessJointNode# Node:" <getName()<( jointResolver.getElement() ); - - //Translation via SID was successful - if ( pTranslate ) - { - extractTranslation( pTranslate, workingTransform ); - } - else - { - //Translation via child from element - daeElement* pTranslateElement = getChildFromElement( pNode, "translate" ); - if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() ) - { - llwarns<< "The found element is not a translate node" <getName() ] = workingTransform; - - //2. handle the nodes children - - //Gather and handle the incoming nodes children - daeTArray< daeSmartRef > childOfChild = pNode->getChildren(); - S32 childOfChildCount = childOfChild.getCount(); - - for (S32 i = 0; i < childOfChildCount; ++i) - { - domNode* pChildNode = daeSafeCast( childOfChild[i] ); - if ( pChildNode ) - { - processJointNode( pChildNode, jointTransforms ); - } - } -} - -daeElement* LLModelLoader::getChildFromElement( daeElement* pElement, std::string const & name ) -{ - daeElement* pChildOfElement = pElement->getChild( name.c_str() ); - if ( pChildOfElement ) - { - return pChildOfElement; - } - llwarns<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << llendl; - return NULL; -} - -void LLModelLoader::processElement(daeElement* element) -{ - LLMatrix4 saved_transform = mTransform; - - domTranslate* translate = daeSafeCast(element); - if (translate) - { - domFloat3 dom_value = translate->getValue(); - - LLMatrix4 translation; - translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2])); - - translation *= mTransform; - mTransform = translation; - } - - domRotate* rotate = daeSafeCast(element); - if (rotate) - { - domFloat4 dom_value = rotate->getValue(); - - LLMatrix4 rotation; - rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0)); - - rotation *= mTransform; - mTransform = rotation; - } - - domScale* scale = daeSafeCast(element); - if (scale) - { - domFloat3 dom_value = scale->getValue(); - - LLMatrix4 scaling; - scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2])); - - scaling *= mTransform; - mTransform = scaling; - } - - domMatrix* matrix = daeSafeCast(element); - if (matrix) - { - domFloat4x4 dom_value = matrix->getValue(); - - LLMatrix4 matrix_transform; - - for (int i = 0; i < 4; i++) - { - for(int j = 0; j < 4; j++) - { - matrix_transform.mMatrix[i][j] = dom_value[i + j*4]; - } - } - - matrix_transform *= mTransform; - mTransform = matrix_transform; - } - - domInstance_geometry* instance_geo = daeSafeCast(element); - if (instance_geo) - { - domGeometry* geo = daeSafeCast(instance_geo->getUrl().getElement()); - if (geo) - { - domMesh* mesh = daeSafeCast(geo->getDescendant(daeElement::matchType(domMesh::ID()))); - if (mesh) - { - LLModel* model = mModel[mesh]; - if (model) - { - LLMatrix4 transformation = mTransform; - - std::vector materials = getMaterials(model, instance_geo); - - // adjust the transformation to compensate for mesh normalization - LLVector3 mesh_scale_vector; - LLVector3 mesh_translation_vector; - model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); - - LLMatrix4 mesh_translation; - mesh_translation.setTranslation(mesh_translation_vector); - mesh_translation *= transformation; - transformation = mesh_translation; - - LLMatrix4 mesh_scale; - mesh_scale.initScale(mesh_scale_vector); - mesh_scale *= transformation; - transformation = mesh_scale; - - mScene[transformation].push_back(LLModelInstance(model, transformation, materials)); - - stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); - } - } - } - } - - domInstance_node* instance_node = daeSafeCast(element); - if (instance_node) - { - daeElement* instance = instance_node->getUrl().getElement(); - if (instance) - { - processElement(instance); - } - } - - //process children - daeTArray< daeSmartRef > children = element->getChildren(); - for (S32 i = 0; i < children.getCount(); i++) - { - processElement(children[i]); - } - - domNode* node = daeSafeCast(element); - if (node) - { //this element was a node, restore transform before processiing siblings - mTransform = saved_transform; - } -} - -std::vector LLModelLoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo) -{ - std::vector materials; - for (int i = 0; i < model->mMaterialList.size(); i++) - { - LLImportMaterial import_material; - - domInstance_material* instance_mat = NULL; - - domBind_material::domTechnique_common* technique = - daeSafeCast(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); - - if (technique) - { - daeTArray< daeSmartRef > inst_materials = technique->getChildrenByType(); - for (int j = 0; j < inst_materials.getCount(); j++) - { - std::string symbol(inst_materials[j]->getSymbol()); - - if (symbol == model->mMaterialList[i]) // found the binding - { - instance_mat = inst_materials[j]; - } - } - } - - if (instance_mat) - { - domMaterial* material = daeSafeCast(instance_mat->getTarget().getElement()); - if (material) - { - domInstance_effect* instance_effect = - daeSafeCast(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); - if (instance_effect) - { - domEffect* effect = daeSafeCast(instance_effect->getUrl().getElement()); - if (effect) - { - domProfile_COMMON* profile = - daeSafeCast(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID()))); - if (profile) - { - import_material = profileToMaterial(profile); - } - } - } - } - } - - materials.push_back(import_material); - } - - return materials; -} - -LLImportMaterial LLModelLoader::profileToMaterial(domProfile_COMMON* material) -{ - LLImportMaterial mat; - mat.mFullbright = FALSE; - - daeElement* diffuse = material->getDescendant("diffuse"); - if (diffuse) - { - domCommon_color_or_texture_type_complexType::domTexture* texture = - daeSafeCast(diffuse->getDescendant("texture")); - if (texture) - { - domCommon_newparam_type_Array newparams = material->getNewparam_array(); - for (S32 i = 0; i < newparams.getCount(); i++) - { - domFx_surface_common* surface = newparams[i]->getSurface(); - if (surface) - { - domFx_surface_init_common* init = surface->getFx_surface_init_common(); - if (init) - { - domFx_surface_init_from_common_Array init_from = init->getInit_from_array(); - - if (init_from.getCount() > i) - { - domImage* image = daeSafeCast(init_from[i]->getValue().getElement()); - if (image) - { - // we only support init_from now - embedded data will come later - domImage::domInit_from* init = image->getInit_from(); - if (init) - { - std::string filename = cdom::uriToNativePath(init->getValue().str()); - - mat.mDiffuseMap = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + filename, TRUE, LLViewerTexture::BOOST_PREVIEW); - mat.mDiffuseMap->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, this->mPreview, NULL, FALSE); - - mat.mDiffuseMap->forceToSaveRawImage(); - mat.mDiffuseMapFilename = filename; - mat.mDiffuseMapLabel = getElementLabel(material); - } - } - } - } - } - } - } - - domCommon_color_or_texture_type_complexType::domColor* color = - daeSafeCast(diffuse->getDescendant("color")); - if (color) - { - domFx_color_common domfx_color = color->getValue(); - LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); - mat.mDiffuseColor = value; - } - } - - daeElement* emission = material->getDescendant("emission"); - if (emission) - { - LLColor4 emission_color = getDaeColor(emission); - if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25) - { - mat.mFullbright = TRUE; - } - } - - return mat; -} - -// try to get a decent label for this element -std::string LLModelLoader::getElementLabel(daeElement *element) -{ - // if we have a name attribute, use it - std::string name = element->getAttribute("name"); - if (name.length()) - { - return name; - } - - // if we have an ID attribute, use it - if (element->getID()) - { - return std::string(element->getID()); - } - - // if we have a parent, use it - daeElement* parent = element->getParent(); - if (parent) - { - // if parent has a name, use it - std::string name = parent->getAttribute("name"); - if (name.length()) - { - return name; - } - - // if parent has an ID, use it - if (parent->getID()) - { - return std::string(parent->getID()); - } - } - - // try to use our type - daeString element_name = element->getElementName(); - if (element_name) - { - return std::string(element_name); - } - - // if all else fails, use "object" - return std::string("object"); -} - -LLColor4 LLModelLoader::getDaeColor(daeElement* element) -{ - LLColor4 value; - domCommon_color_or_texture_type_complexType::domColor* color = - daeSafeCast(element->getDescendant("color")); - if (color) - { - domFx_color_common domfx_color = color->getValue(); - value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); - } - - return value; -} - -//----------------------------------------------------------------------------- -// LLModelPreview -//----------------------------------------------------------------------------- - -LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) -: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex(NULL) -{ - mNeedsUpdate = TRUE; - mCameraDistance = 0.f; - mCameraYaw = 0.f; - mCameraPitch = 0.f; - mCameraZoom = 1.f; - mTextureName = 0; - mPreviewLOD = 0; - mModelLoader = NULL; - mMaxTriangleLimit = 0; - mDirty = false; - mGenLOD = false; - mLoading = false; - mGroup = 0; - mBuildShareTolerance = 0.f; - mBuildQueueMode = GLOD_QUEUE_GREEDY; - mBuildBorderMode = GLOD_BORDER_UNLOCK; - mBuildOperator = GLOD_OPERATOR_HALF_EDGE_COLLAPSE; - - mViewOption["show_textures"] = false; - - mFMP = fmp; - - glodInit(); -} - -LLModelPreview::~LLModelPreview() -{ - if (mModelLoader) - { - delete mModelLoader; - mModelLoader = NULL; - } - - //*HACK : *TODO : turn this back on when we understand why this crashes - //glodShutdown(); -} - -U32 LLModelPreview::calcResourceCost() -{ - assert_main_thread(); - - rebuildUploadData(); - - if ( mModelLoader->getLoadState() != LLModelLoader::ERROR_PARSING ) - { - mFMP->childEnable("ok_btn"); - } - - U32 cost = 0; - std::set accounted; - U32 num_points = 0; - U32 num_hulls = 0; - - F32 debug_scale = mFMP->childGetValue("import_scale").asReal(); - - F32 streaming_cost = 0.f; - F32 physics_cost = 0.f; - for (U32 i = 0; i < mUploadData.size(); ++i) - { - LLModelInstance& instance = mUploadData[i]; - - if (accounted.find(instance.mModel) == accounted.end()) - { - accounted.insert(instance.mModel); - - LLModel::convex_hull_decomposition& decomp = - instance.mLOD[LLModel::LOD_PHYSICS] ? - instance.mLOD[LLModel::LOD_PHYSICS]->mConvexHullDecomp : - instance.mModel->mConvexHullDecomp; - - LLSD ret = LLModel::writeModel( - "", - instance.mLOD[4], - instance.mLOD[3], - instance.mLOD[2], - instance.mLOD[1], - instance.mLOD[0], - decomp, - mFMP->childGetValue("upload_skin").asBoolean(), - mFMP->childGetValue("upload_joints").asBoolean(), - TRUE); - cost += gMeshRepo.calcResourceCost(ret); - - num_hulls += decomp.size(); - for (U32 i = 0; i < decomp.size(); ++i) - { - num_points += decomp[i].size(); - } - - //calculate streaming cost - LLMatrix4 transformation = instance.mTransform; - - LLVector3 position = LLVector3(0, 0, 0) * transformation; - - LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - LLVector3 scale = LLVector3(x_length, y_length, z_length); - - F32 radius = scale.length()*debug_scale; - - streaming_cost += LLMeshRepository::getStreamingCost(ret, radius); - } - } - - //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",num_hulls)); - //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",num_points)); - mFMP->childSetTextArg("streaming cost", "[COST]", llformat("%.3f", streaming_cost)); - mFMP->childSetTextArg("physics cost", "[COST]", llformat("%.3f", physics_cost)); - F32 scale = mFMP->childGetValue("import_scale").asReal()*2.f; - mFMP->childSetTextArg("import_dimensions", "[X]", llformat("%.3f", mPreviewScale[0]*scale)); - mFMP->childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", mPreviewScale[1]*scale)); - mFMP->childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", mPreviewScale[2]*scale)); - - updateStatusMessages(); - - return cost; -} - -void LLModelPreview::rebuildUploadData() -{ - assert_main_thread(); - - mUploadData.clear(); - mTextureSet.clear(); - - //fill uploaddata instance vectors from scene data - - std::string requested_name = mFMP->getChild("description_form")->getValue().asString(); - - - LLSpinCtrl* scale_spinner = mFMP->getChild("import_scale"); - - if (!scale_spinner) - { - llerrs << "floater_model_preview.xml MUST contain import_scale spinner." << llendl; - } - - F32 scale = scale_spinner->getValue().asReal(); - - LLMatrix4 scale_mat; - scale_mat.initScale(LLVector3(scale, scale, scale)); - - F32 max_scale = 0.f; - - if ( mBaseScene.size() > 0 ) - { - mFMP->childEnable("ok_btn"); - } - - for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) - { //for each transform in scene - LLMatrix4 mat = iter->first; - - // compute position - LLVector3 position = LLVector3(0, 0, 0) * mat; - - // compute scale - LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - - max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length); - - mat *= scale_mat; - - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { //for each instance with said transform applied - LLModelInstance instance = *model_iter; - - LLModel* base_model = instance.mModel; - if (base_model) - { - base_model->mRequestedLabel = requested_name; - } - - S32 idx = 0; - for (idx = 0; idx < mBaseModel.size(); ++idx) - { //find reference instance for this model - if (mBaseModel[idx] == base_model) - { - break; - } - } - - for (U32 i = 0; i < LLModel::NUM_LODS; i++) - { //fill LOD slots based on reference model index - if (!mModel[i].empty()) - { - instance.mLOD[i] = mModel[i][idx]; - } - else - { - instance.mLOD[i] = NULL; - } - } - - instance.mTransform = mat; - mUploadData.push_back(instance); - } - } - - F32 max_import_scale = DEFAULT_MAX_PRIM_SCALE/max_scale; - - scale_spinner->setMaxValue(max_import_scale); - - if (max_import_scale < scale) - { - scale_spinner->setValue(max_import_scale); - } - -} - - -void LLModelPreview::clearModel(S32 lod) -{ - if (lod < 0 || lod > LLModel::LOD_PHYSICS) - { - return; - } - - mVertexBuffer[lod].clear(); - mModel[lod].clear(); - mScene[lod].clear(); -} - -void LLModelPreview::loadModel(std::string filename, S32 lod) -{ - assert_main_thread(); - - LLMutexLock lock(this); - - if (mModelLoader) - { - delete mModelLoader; - mModelLoader = NULL; - } - - if (filename.empty()) - { - if (mBaseModel.empty()) - { - // this is the initial file picking. Close the whole floater - // if we don't have a base model to show for high LOD. - mFMP->closeFloater(false); - } - - mLoading = false; - return; - } - - mLODFile[lod] = filename; - - if (lod == LLModel::LOD_HIGH) - { - clearGLODGroup(); - } - - mModelLoader = new LLModelLoader(filename, lod, this); - - mModelLoader->start(); - - mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); - - setPreviewLOD(lod); - - if ( mModelLoader->getLoadState() == LLModelLoader::ERROR_PARSING ) - { - mFMP->childDisable("ok_btn"); - } - - if (lod == mPreviewLOD) - { - mFMP->childSetText("lod_file", mLODFile[mPreviewLOD]); - } - else if (lod == LLModel::LOD_PHYSICS) - { - mFMP->childSetText("physics_file", mLODFile[lod]); - } - - mFMP->openFloater(); -} - -void LLModelPreview::setPhysicsFromLOD(S32 lod) -{ - assert_main_thread(); - - if (lod >= 0 && lod <= 3) - { - mModel[LLModel::LOD_PHYSICS] = mModel[lod]; - mScene[LLModel::LOD_PHYSICS] = mScene[lod]; - mLODFile[LLModel::LOD_PHYSICS].clear(); - mFMP->childSetText("physics_file", mLODFile[LLModel::LOD_PHYSICS]); - mVertexBuffer[LLModel::LOD_PHYSICS].clear(); - rebuildUploadData(); - refresh(); - updateStatusMessages(); - } -} - -void LLModelPreview::clearIncompatible(S32 lod) -{ - for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) - { //clear out any entries that aren't compatible with this model - if (i != lod) - { - if (mModel[i].size() != mModel[lod].size()) - { - mModel[i].clear(); - mScene[i].clear(); - mVertexBuffer[i].clear(); - - if (i == LLModel::LOD_HIGH) - { - mBaseModel = mModel[lod]; - clearGLODGroup(); - mBaseScene = mScene[lod]; - mVertexBuffer[5].clear(); - } - } - } - } -} - -void LLModelPreview::clearGLODGroup() -{ - if (mGroup) - { - for (std::map, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) - { - glodDeleteObject(iter->second); - stop_gloderror(); - } - mObject.clear(); - - glodDeleteGroup(mGroup); - stop_gloderror(); - mGroup = 0; - } -} - -void LLModelPreview::loadModelCallback(S32 lod) -{ - assert_main_thread(); - - LLMutexLock lock(this); - if (!mModelLoader) - { - return; - } - - mModel[lod] = mModelLoader->mModelList; - mScene[lod] = mModelLoader->mScene; - mVertexBuffer[lod].clear(); - - if (lod == LLModel::LOD_PHYSICS) - { - mPhysicsMesh.clear(); - } - - setPreviewLOD(lod); - - - if (lod == LLModel::LOD_HIGH) - { //save a copy of the highest LOD for automatic LOD manipulation - if (mBaseModel.empty()) - { //first time we've loaded a model, auto-gen LoD - mGenLOD = true; - } - - mBaseModel = mModel[lod]; - clearGLODGroup(); - - mBaseScene = mScene[lod]; - mVertexBuffer[5].clear(); - } - - clearIncompatible(lod); - - mDirty = true; - - if (lod == LLModel::LOD_HIGH) - { - resetPreviewTarget(); - } - - mLoading = false; - refresh(); -} - -void LLModelPreview::resetPreviewTarget() -{ - mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; - mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; - setPreviewTarget(mPreviewScale.magVec()*2.f); -} - -void LLModelPreview::generateNormals() -{ - assert_main_thread(); - - S32 which_lod = mPreviewLOD; - - - if (which_lod > 4 || which_lod < 0 || - mModel[which_lod].empty()) - { - return; - } - - F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); - - angle_cutoff *= DEG_TO_RAD; - - if (which_lod == 3 && !mBaseModel.empty()) - { - for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) - { - (*iter)->generateNormals(angle_cutoff); - } - - mVertexBuffer[5].clear(); - } - - for (LLModelLoader::model_list::iterator iter = mModel[which_lod].begin(); iter != mModel[which_lod].end(); ++iter) - { - (*iter)->generateNormals(angle_cutoff); - } - - mVertexBuffer[which_lod].clear(); - refresh(); - -} - -void LLModelPreview::consolidate() -{ - std::map > composite; - - LLMatrix4 identity; - - //bake out each node in current scene to composite - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { //for each transform in current scene - LLMatrix4 mat = iter->first; - glh::matrix4f inv_trans = glh::matrix4f((F32*) mat.mMatrix).inverse().transpose(); - LLMatrix4 norm_mat(inv_trans.m); - - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { //for each instance with that transform - LLModelInstance& source_instance = *model_iter; - LLModel* source = source_instance.mModel; - - if (!validate_model(source)) - { - llerrs << "Invalid model found!" << llendl; - } - - for (S32 i = 0; i < source->getNumVolumeFaces(); ++i) - { //for each face in instance - const LLVolumeFace& src_face = source->getVolumeFace(i); - LLImportMaterial& source_material = source_instance.mMaterial[i]; - - //get model in composite that is composite for this material - LLModel* model = NULL; - - if (composite.find(source_material) != composite.end()) - { - model = composite[source_material].rbegin()->mModel; - if (model->getVolumeFace(0).mNumVertices + src_face.mNumVertices > 65535) - { - model = NULL; - } - } - - if (model == NULL) - { //no model found, make new model - std::vector materials; - materials.push_back(source_material); - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - model = new LLModel(volume_params, 0.f); - model->mLabel = source->mLabel; - model->setNumVolumeFaces(0); - composite[source_material].push_back(LLModelInstance(model, identity, materials)); - } - - model->appendFace(src_face, source->mMaterialList[i], mat, norm_mat); - } - } - } - - - //condense composite into as few LLModel instances as possible - LLModelLoader::model_list new_model; - std::vector instance_list; - - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - - std::vector empty_material; - LLModelInstance cur_instance(new LLModel(volume_params, 0.f), identity, empty_material); - cur_instance.mModel->setNumVolumeFaces(0); - - BOOL first_transform = TRUE; - - LLModelLoader::scene new_scene; - LLVector3 min,max; - - for (std::map >::iterator iter = composite.begin(); - iter != composite.end(); - ++iter) - { - std::map >::iterator next_iter = iter; ++next_iter; - - for (std::vector::iterator instance_iter = iter->second.begin(); - instance_iter != iter->second.end(); - ++instance_iter) - { - LLModel* source = instance_iter->mModel; - - if (instance_iter->mMaterial.size() != 1) - { - llerrs << "WTF?" << llendl; - } - - if (source->getNumVolumeFaces() != 1) - { - llerrs << "WTF?" << llendl; - } - - if (source->mMaterialList.size() != 1) - { - llerrs << "WTF?" << llendl; - } - - cur_instance.mModel->addFace(source->getVolumeFace(0)); - cur_instance.mMaterial.push_back(instance_iter->mMaterial[0]); - cur_instance.mModel->mMaterialList.push_back(source->mMaterialList[0]); - - BOOL last_model = FALSE; - - std::vector::iterator next_instance = instance_iter; ++next_instance; - - if (next_iter == composite.end() && - next_instance == iter->second.end()) - { - last_model = TRUE; - } - - if (last_model || cur_instance.mModel->getNumVolumeFaces() >= MAX_MODEL_FACES) - { - cur_instance.mModel->mLabel = source->mLabel; - - cur_instance.mModel->optimizeVolumeFaces(); - cur_instance.mModel->normalizeVolumeFaces(); - - if (!validate_model(cur_instance.mModel)) - { - llerrs << "Invalid model detected." << llendl; - } - - new_model.push_back(cur_instance.mModel); - - LLMatrix4 transformation = LLMatrix4(); - - // adjust the transformation to compensate for mesh normalization - LLVector3 mesh_scale_vector; - LLVector3 mesh_translation_vector; - cur_instance.mModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); - - LLMatrix4 mesh_translation; - mesh_translation.setTranslation(mesh_translation_vector); - mesh_translation *= transformation; - transformation = mesh_translation; - - LLMatrix4 mesh_scale; - mesh_scale.initScale(mesh_scale_vector); - mesh_scale *= transformation; - transformation = mesh_scale; - - cur_instance.mTransform = transformation; - - new_scene[transformation].push_back(cur_instance); - stretch_extents(cur_instance.mModel, transformation, min, max, first_transform); - - if (!last_model) - { - cur_instance = LLModelInstance(new LLModel(volume_params, 0.f), identity, empty_material); - cur_instance.mModel->setNumVolumeFaces(0); - } - } - } - } - - mScene[mPreviewLOD] = new_scene; - mModel[mPreviewLOD] = new_model; - mVertexBuffer[mPreviewLOD].clear(); - - if (mPreviewLOD == LLModel::LOD_HIGH) - { - mBaseScene = new_scene; - mBaseModel = new_model; - clearGLODGroup(); - mVertexBuffer[5].clear(); - } - - mPreviewTarget = (min+max)*0.5f; - mPreviewScale = (max-min)*0.5f; - setPreviewTarget(mPreviewScale.magVec()*2.f); - - clearIncompatible(mPreviewLOD); - - mResourceCost = calcResourceCost(); - refresh(); -} - -void LLModelPreview::clearMaterials() -{ - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { //for each transform in current scene - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { //for each instance with that transform - LLModelInstance& source_instance = *model_iter; - LLModel* source = source_instance.mModel; - - for (S32 i = 0; i < source->getNumVolumeFaces(); ++i) - { //for each face in instance - LLImportMaterial& source_material = source_instance.mMaterial[i]; - - //clear material info - source_material.mDiffuseColor = LLColor4(1,1,1,1); - source_material.mDiffuseMap = NULL; - source_material.mDiffuseMapFilename.clear(); - source_material.mDiffuseMapLabel.clear(); - source_material.mFullbright = false; - } - } - } - - mVertexBuffer[mPreviewLOD].clear(); - - if (mPreviewLOD == LLModel::LOD_HIGH) - { - mBaseScene = mScene[mPreviewLOD]; - mBaseModel = mModel[mPreviewLOD]; - clearGLODGroup(); - mVertexBuffer[5].clear(); - } - - mResourceCost = calcResourceCost(); - refresh(); -} - -bool LLModelPreview::containsRiggedAsset( void ) -{ - //loop through the models and determine if any of them contained a rigged asset, and if so - //return true. - //This is used to cleanup the joint positions after a preview. - for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) - { - LLModel* pModel = *iter; - if ( pModel->mAlternateBindMatrix.size() > 0 ) - { - return true; - } - } - return false; -} -void LLModelPreview::genLODs(S32 which_lod, U32 decimation) -{ - if (mBaseModel.empty()) - { - return; - } - - if (which_lod == LLModel::LOD_PHYSICS) - { //clear physics mesh map - mPhysicsMesh.clear(); - } - - LLVertexBuffer::unbind(); - - stop_gloderror(); - static U32 cur_name = 1; - - S32 limit = -1; - - U32 triangle_count = 0; - - for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) - { - LLModel* mdl = *iter; - for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) - { - triangle_count += mdl->getVolumeFace(i).mNumIndices/3; - } - } - - U32 base_triangle_count = triangle_count; - - U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - U32 lod_mode = 0; - - LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode"); - if (iface) - { - lod_mode = iface->getFirstSelectedIndex(); - } - - F32 lod_error_threshold = mFMP->childGetValue("lod_error_threshold").asReal(); - - if (lod_mode == 0) - { - lod_mode = GLOD_TRIANGLE_BUDGET; - if (which_lod != -1) - { - limit = mFMP->childGetValue("lod_triangle_limit").asInteger(); - } - } - else - { - lod_mode = GLOD_ERROR_THRESHOLD; - } - - U32 build_operator = 0; - - iface = mFMP->childGetSelectionInterface("build_operator"); - if (iface) - { - build_operator = iface->getFirstSelectedIndex(); - } - - if (build_operator == 0) - { - build_operator = GLOD_OPERATOR_HALF_EDGE_COLLAPSE; - } - else - { - build_operator = GLOD_OPERATOR_EDGE_COLLAPSE; - } - - U32 queue_mode=0; - iface = mFMP->childGetSelectionInterface("queue_mode"); - if (iface) - { - queue_mode = iface->getFirstSelectedIndex(); - } - - if (queue_mode == 0) - { - queue_mode = GLOD_QUEUE_GREEDY; - } - else if (queue_mode == 1) - { - queue_mode = GLOD_QUEUE_LAZY; - } - else - { - queue_mode = GLOD_QUEUE_INDEPENDENT; - } - - U32 border_mode = 0; - - iface = mFMP->childGetSelectionInterface("border_mode"); - if (iface) - { - border_mode = iface->getFirstSelectedIndex(); - } - - if (border_mode == 0) - { - border_mode = GLOD_BORDER_UNLOCK; - } - else - { - border_mode = GLOD_BORDER_LOCK; - } - - bool object_dirty = false; - if (border_mode != mBuildBorderMode) - { - mBuildBorderMode = border_mode; - object_dirty = true; - } - - if (queue_mode != mBuildQueueMode) - { - mBuildQueueMode = queue_mode; - object_dirty = true; - } - - if (build_operator != mBuildOperator) - { - mBuildOperator = build_operator; - object_dirty = true; - } - - F32 share_tolerance = mFMP->childGetValue("share_tolerance").asReal(); - if (share_tolerance != mBuildShareTolerance) - { - mBuildShareTolerance = share_tolerance; - object_dirty = true; - } - - if (mGroup == 0) - { - object_dirty = true; - mGroup = cur_name++; - glodNewGroup(mGroup); - } - - if (object_dirty) - { - for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) - { //build GLOD objects for each model in base model list - LLModel* mdl = *iter; - - if (mObject[mdl] != 0) - { - glodDeleteObject(mObject[mdl]); - } - - mObject[mdl] = cur_name++; - - glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); - stop_gloderror(); - - if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) - { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation - mVertexBuffer[5].clear(); - } - - if (mVertexBuffer[5].empty()) - { - genBuffers(5, false); - } - - U32 tri_count = 0; - for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) - { - mVertexBuffer[5][mdl][i]->setBuffer(type_mask); - U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); - if (num_indices > 2) - { - glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); - } - tri_count += num_indices/3; - stop_gloderror(); - } - - glodObjectParameteri(mObject[mdl], GLOD_BUILD_OPERATOR, build_operator); - stop_gloderror(); - - glodObjectParameteri(mObject[mdl], GLOD_BUILD_QUEUE_MODE, queue_mode); - stop_gloderror(); - - glodObjectParameteri(mObject[mdl], GLOD_BUILD_BORDER_MODE, border_mode); - stop_gloderror(); - - glodObjectParameterf(mObject[mdl], GLOD_BUILD_SHARE_TOLERANCE, share_tolerance); - stop_gloderror(); - - glodBuildObject(mObject[mdl]); - stop_gloderror(); - } - } - - - S32 start = LLModel::LOD_HIGH; - S32 end = 0; - - if (which_lod != -1) - { - start = end = which_lod; - } - else - { - //SH-632 -- incremenet triangle count to avoid removing any triangles from - //highest LoD when auto-generating LoD - triangle_count++; - } - - - mMaxTriangleLimit = base_triangle_count; - - for (S32 lod = start; lod >= end; --lod) - { - if (which_lod == -1) - { - if (lod < start) - { - triangle_count /= decimation; - } - } - else - { - triangle_count = limit; - } - - mModel[lod].clear(); - mModel[lod].resize(mBaseModel.size()); - mVertexBuffer[lod].clear(); - - U32 actual_tris = 0; - U32 actual_verts = 0; - U32 submeshes = 0; - - glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); - stop_gloderror(); - - glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); - stop_gloderror(); - - glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count); - stop_gloderror(); - - glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); - stop_gloderror(); - - glodAdaptGroup(mGroup); - stop_gloderror(); - - for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) - { - LLModel* base = mBaseModel[mdl_idx]; - - GLint patch_count = 0; - glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); - stop_gloderror(); - - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); - - GLint* sizes = new GLint[patch_count*2]; - glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); - stop_gloderror(); - - GLint* names = new GLint[patch_count]; - glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); - stop_gloderror(); - - mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); - - LLModel* target_model = mModel[lod][mdl_idx]; - - for (GLint i = 0; i < patch_count; ++i) - { - LLPointer buff = new LLVertexBuffer(type_mask, 0); - - if (sizes[i*2+1] > 0 && sizes[i*2] > 0) - { - buff->allocateBuffer(sizes[i*2+1], sizes[i*2], true); - buff->setBuffer(type_mask); - glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, buff->getIndicesPointer()); - stop_gloderror(); - } - else - { //this face was eliminated, create a dummy triangle (one vertex, 3 indices, all 0) - buff->allocateBuffer(1, 3, true); - memset(buff->getMappedData(), 0, buff->getSize()); - memset(buff->getIndicesPointer(), 0, buff->getIndicesSize()); - } - - buff->validateRange(0, buff->getNumVerts()-1, buff->getNumIndices(), 0); - - LLStrider pos; - LLStrider norm; - LLStrider tc; - LLStrider index; - - buff->getVertexStrider(pos); - buff->getNormalStrider(norm); - buff->getTexCoord0Strider(tc); - buff->getIndexStrider(index); - - target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); - actual_tris += buff->getNumIndices()/3; - actual_verts += buff->getNumVerts(); - ++submeshes; - - if (!validate_face(target_model->getVolumeFace(names[i]))) - { - llerrs << "Invalid face generated during LOD generation." << llendl; - } - } - - //blind copy skin weights and just take closest skin weight to point on - //decimated mesh for now (auto-generating LODs with skin weights is still a bit - //of an open problem). - target_model->mPosition = base->mPosition; - target_model->mSkinWeights = base->mSkinWeights; - target_model->mJointMap = base->mJointMap; - target_model->mJointList = base->mJointList; - target_model->mInvBindMatrix = base->mInvBindMatrix; - target_model->mBindShapeMatrix = base->mBindShapeMatrix; - target_model->mAlternateBindMatrix = base->mAlternateBindMatrix; - //copy material list - target_model->mMaterialList = base->mMaterialList; - - if (!validate_model(target_model)) - { - llerrs << "Invalid model generated when creating LODs" << llendl; - } - - delete [] sizes; - delete [] names; - } - - //rebuild scene based on mBaseScene - mScene[lod].clear(); - mScene[lod] = mBaseScene; - - for (U32 i = 0; i < mBaseModel.size(); ++i) - { - LLModel* mdl = mBaseModel[i]; - LLModel* target = mModel[lod][i]; - if (target) - { - for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) - { - for (U32 j = 0; j < iter->second.size(); ++j) - { - if (iter->second[j].mModel == mdl) - { - iter->second[j].mModel = target; - } - } - } - } - } - } - - mResourceCost = calcResourceCost(); - - /*if (which_lod == -1 && mScene[LLModel::LOD_PHYSICS].empty()) - { //build physics scene - mScene[LLModel::LOD_PHYSICS] = mScene[LLModel::LOD_LOW]; - mModel[LLModel::LOD_PHYSICS] = mModel[LLModel::LOD_LOW]; - - for (U32 i = 1; i < mModel[LLModel::LOD_PHYSICS].size(); ++i) - { - mPhysicsQ.push(mModel[LLModel::LOD_PHYSICS][i]); - } - }*/ -} - -void LLModelPreview::updateStatusMessages() -{ - assert_main_thread(); - - //triangle/vertex/submesh count for each mesh asset for each lod - std::vector tris[LLModel::NUM_LODS]; - std::vector verts[LLModel::NUM_LODS]; - std::vector submeshes[LLModel::NUM_LODS]; - - //total triangle/vertex/submesh count for each lod - S32 total_tris[LLModel::NUM_LODS]; - S32 total_verts[LLModel::NUM_LODS]; - S32 total_submeshes[LLModel::NUM_LODS]; - - for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) - { - //initialize total for this lod to 0 - total_tris[lod] = total_verts[lod] = total_submeshes[lod] = 0; - - for (U32 i = 0; i < mModel[lod].size(); ++i) - { //for each model in the lod - S32 cur_tris = 0; - S32 cur_verts = 0; - S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); - - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); - cur_tris += face.mNumIndices/3; - cur_verts += face.mNumVertices; - } - - //add this model to the lod total - total_tris[lod] += cur_tris; - total_verts[lod] += cur_verts; - total_submeshes[lod] += cur_submeshes; - - //store this model's counts to asset data - tris[lod].push_back(cur_tris); - verts[lod].push_back(cur_verts); - submeshes[lod].push_back(cur_submeshes); - } - } - - if (mMaxTriangleLimit == 0) - { - mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; - } - - - mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH])); - - std::string mesh_status_na = mFMP->getString("mesh_status_na"); - - S32 upload_status[LLModel::LOD_HIGH+1]; - - bool upload_ok = true; - - for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod) - { - upload_status[lod] = 0; - - std::string message = "mesh_status_good"; - - if (total_tris[lod] > 0) - { - mFMP->childSetText(lod_triangles_name[lod], llformat("%d", total_tris[lod])); - mFMP->childSetText(lod_vertices_name[lod], llformat("%d", total_verts[lod])); - } - else - { - if (lod == LLModel::LOD_HIGH) - { - upload_status[lod] = 2; - message = "mesh_status_missing_lod"; - } - else - { - for (S32 i = lod-1; i >= 0; --i) - { - if (total_tris[i] > 0) - { - upload_status[lod] = 2; - message = "mesh_status_missing_lod"; - } - } - } - - mFMP->childSetText(lod_triangles_name[lod], mesh_status_na); - mFMP->childSetText(lod_vertices_name[lod], mesh_status_na); - } - - const U32 lod_high = LLModel::LOD_HIGH; - - if (lod != lod_high) - { - if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) - { //number of submeshes is different - message = "mesh_status_submesh_mismatch"; - upload_status[lod] = 2; - } - else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) - { //number of meshes is different - message = "mesh_status_mesh_mismatch"; - upload_status[lod] = 2; - } - else if (!verts[lod].empty()) - { - for (U32 i = 0; i < verts[lod].size(); ++i) - { - S32 max_verts = i < verts[lod+1].size() ? verts[lod+1][i] : 0; - - if (max_verts > 0) - { - if (verts[lod][i] > max_verts) - { //too many vertices in this lod - message = "mesh_status_too_many_vertices"; - upload_status[lod] = 2; - } - } - } - } - } - - LLIconCtrl* icon = mFMP->getChild(lod_icon_name[lod]); - LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]); - icon->setVisible(true); - icon->setImage(img); - - if (upload_status[lod] >= 2) - { - upload_ok = false; - } - - if (lod == mPreviewLOD) - { - mFMP->childSetText("lod_status_message_text", mFMP->getString(message)); - icon = mFMP->getChild("lod_status_message_icon"); - icon->setImage(img); - } - } - - bool errorStateFromLoader = mModelLoader->getLoadState() == LLModelLoader::ERROR_PARSING ? true : false; - - if ( upload_ok && !errorStateFromLoader ) - { - mFMP->childEnable("ok_btn"); - } - else - { - mFMP->childDisable("ok_btn"); - } - - //add up physics triangles etc - S32 start = 0; - S32 end = mModel[LLModel::LOD_PHYSICS].size(); - - S32 phys_tris = 0; - S32 phys_hulls = 0; - S32 phys_points = 0; - - for (S32 i = start; i < end; ++i) - { //add up hulls and points and triangles for selected mesh(es) - LLModel* model = mModel[LLModel::LOD_PHYSICS][i]; - S32 cur_submeshes = model->getNumVolumeFaces(); - - LLModel::convex_hull_decomposition& decomp = model->mConvexHullDecomp; - - if (!decomp.empty()) - { - phys_hulls += decomp.size(); - for (U32 i = 0; i < decomp.size(); ++i) - { - phys_points += decomp[i].size(); - } - } - else - { //choose physics shape OR decomposition, can't use both - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = model->getVolumeFace(j); - phys_tris += face.mNumIndices/3; - } - } - } - - if (phys_tris > 0) - { - mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", llformat("%d", phys_tris)); - } - else - { - mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", mesh_status_na); - } - - if (phys_hulls > 0) - { - mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls)); - mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points)); - } - else - { - mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na); - mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na); - } - - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp) - { - if (phys_tris > 0 || phys_hulls > 0) - { - if (!fmp->isViewOptionEnabled("show_physics")) - { - fmp->enableViewOption("show_physics"); - mViewOption["show_physics"] = true; - } - } - else - { - fmp->disableViewOption("show_physics"); - mViewOption["show_physics"] = false; - - } - - //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean(); - - //fmp->childSetEnabled("physics_optimize", !use_hull); - - bool enable = phys_tris > 0 || phys_hulls > 0; - //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); - - //enable/disable "analysis" UI - LLPanel* panel = fmp->getChild("physics analysis"); - LLView* child = panel->getFirstChild(); - while (child) - { - child->setEnabled(enable); - child = panel->findNextSibling(child); - } - - enable = phys_hulls > 0; - //enable/disable "simplification" UI - panel = fmp->getChild("physics simplification"); - child = panel->getFirstChild(); - while (child) - { - child->setEnabled(enable); - child = panel->findNextSibling(child); - } - } - - const char* lod_controls[] = - { - "lod_mode", - "lod_triangle_limit", - "lod_error_tolerance", - "build_operator_text", - "queue_mode_text", - "border_mode_text", - "share_tolerance_text", - "build_operator", - "queue_mode", - "border_mode", - "share_tolerance" - }; - const U32 num_lod_controls = sizeof(lod_controls)/sizeof(char*); - - const char* file_controls[] = - { - "lod_browse", - "lod_file" - }; - const U32 num_file_controls = sizeof(file_controls)/sizeof(char*); - - if (fmp) - { - //enable/disable controls based on radio groups - if (mFMP->childGetValue("lod_from_file").asBoolean()) - { - fmp->mLODMode[mPreviewLOD] = 0; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childEnable(file_controls[i]); - } - - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childDisable(lod_controls[i]); - } - } - else if (mFMP->childGetValue("lod_none").asBoolean()) - { - fmp->mLODMode[mPreviewLOD] = 2; - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childDisable(file_controls[i]); - } - - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childDisable(lod_controls[i]); - } - - if (!mModel[mPreviewLOD].empty()) - { - mModel[mPreviewLOD].clear(); - mScene[mPreviewLOD].clear(); - mVertexBuffer[mPreviewLOD].clear(); - - //this can cause phasing issues with the UI, so reenter this function and return - updateStatusMessages(); - return; - } - } - else - { // auto generate, also the default case for wizard which has no radio selection - fmp->mLODMode[mPreviewLOD] = 1; - - for (U32 i = 0; i < num_file_controls; ++i) - { - mFMP->childDisable(file_controls[i]); - } - - for (U32 i = 0; i < num_lod_controls; ++i) - { - mFMP->childEnable(lod_controls[i]); - } - - //if (threshold) - { - U32 lod_mode = 0; - LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode"); - if (iface) - { - lod_mode = iface->getFirstSelectedIndex(); - } - - LLSpinCtrl* threshold = mFMP->getChild("lod_error_threshold"); - LLSpinCtrl* limit = mFMP->getChild("lod_triangle_limit"); - - limit->setMaxValue(mMaxTriangleLimit); - limit->setValue(total_tris[mPreviewLOD]); - - if (lod_mode == 0) - { - limit->setVisible(true); - threshold->setVisible(false); - - limit->setMaxValue(mMaxTriangleLimit); - } - else - { - limit->setVisible(false); - threshold->setVisible(true); - } - } - } - } - - if (mFMP->childGetValue("physics_load_from_file").asBoolean()) - { - mFMP->childDisable("physics_lod_combo"); - mFMP->childEnable("physics_file"); - mFMP->childEnable("physics_browse"); - } - else - { - mFMP->childEnable("physics_lod_combo"); - mFMP->childDisable("physics_file"); - mFMP->childDisable("physics_browse"); - } -} - -void LLModelPreview::setPreviewTarget(F32 distance) -{ - mCameraDistance = distance; - mCameraZoom = 1.f; - mCameraPitch = 0.f; - mCameraYaw = 0.f; - mCameraOffset.clearVec(); -} - -void LLModelPreview::clearBuffers() -{ - for (U32 i = 0; i < 6; i++) - { - mVertexBuffer[i].clear(); - } -} - -void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) -{ - U32 tri_count = 0; - U32 vertex_count = 0; - U32 mesh_count = 0; - - LLModelLoader::model_list* model = NULL; - - if (lod < 0 || lod > 4) - { - model = &mBaseModel; - lod = 5; - } - else - { - model = &(mModel[lod]); - } - - if (!mVertexBuffer[lod].empty()) - { - mVertexBuffer[lod].clear(); - } - - mVertexBuffer[lod].clear(); - - LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); - - for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) - { - LLModel* mdl = *iter; - if (!mdl) - { - continue; - } - - LLModel* base_mdl = *base_iter; - base_iter++; - - for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) - { - const LLVolumeFace &vf = mdl->getVolumeFace(i); - U32 num_vertices = vf.mNumVertices; - U32 num_indices = vf.mNumIndices; - - if (!num_vertices || ! num_indices) - { - continue; - } - - LLVertexBuffer* vb = NULL; - - bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); - - U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; - - if (skinned) - { - mask |= LLVertexBuffer::MAP_WEIGHT4; - } - - vb = new LLVertexBuffer(mask, 0); - - vb->allocateBuffer(num_vertices, num_indices, TRUE); - - LLStrider vertex_strider; - LLStrider normal_strider; - LLStrider tc_strider; - LLStrider index_strider; - LLStrider weights_strider; - - vb->getVertexStrider(vertex_strider); - vb->getNormalStrider(normal_strider); - vb->getTexCoord0Strider(tc_strider); - vb->getIndexStrider(index_strider); - - if (skinned) - { - vb->getWeight4Strider(weights_strider); - } - - LLVector4a::memcpyNonAliased16((F32*) vertex_strider.get(), (F32*) vf.mPositions, num_vertices*4*sizeof(F32)); - LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32)); - LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32)); - - if (skinned) - { - for (U32 i = 0; i < num_vertices; i++) - { - //find closest weight to vf.mVertices[i].mPosition - LLVector3 pos(vf.mPositions[i].getF32ptr()); - - const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); - - LLVector4 w(0,0,0,0); - if (weight_list.size() > 4) - { - llerrs << "WTF?" << llendl; - } - - for (U32 i = 0; i < weight_list.size(); ++i) - { - F32 wght = llmin(weight_list[i].mWeight, 0.999999f); - F32 joint = (F32) weight_list[i].mJointIdx; - w.mV[i] = joint + wght; - } - - *(weights_strider++) = w; - } - } - - // build indices - for (U32 i = 0; i < num_indices; i++) - { - *(index_strider++) = vf.mIndices[i]; - } - - mVertexBuffer[lod][mdl].push_back(vb); - - vertex_count += num_vertices; - tri_count += num_indices/3; - ++mesh_count; - - } - } -} - -void LLModelPreview::update() -{ - if (mDirty) - { - mDirty = false; - mResourceCost = calcResourceCost(); - refresh(); - updateStatusMessages(); - } - - if (mGenLOD) - { - mGenLOD = false; - genLODs(); - refresh(); - updateStatusMessages(); - } - -} - -//----------------------------------------------------------------------------- -// render() -//----------------------------------------------------------------------------- -BOOL LLModelPreview::render() -{ - assert_main_thread(); - - LLMutexLock lock(this); - mNeedsUpdate = FALSE; - - bool edges = mViewOption["show_edges"]; - bool joint_positions = mViewOption["show_joint_positions"]; - bool skin_weight = mViewOption["show_skin_weight"]; - bool textures = mViewOption["show_textures"]; - bool physics = mViewOption["show_physics"]; - - S32 width = getWidth(); - S32 height = getHeight(); - - LLGLSUIDefault def; - LLGLDisable no_blend(GL_BLEND); - LLGLEnable cull(GL_CULL_FACE); - LLGLDepthTest depth(GL_TRUE); - LLGLDisable fog(GL_FOG); - - { - //clear background to blue - glMatrixMode(GL_PROJECTION); - gGL.pushMatrix(); - glLoadIdentity(); - glOrtho(0.0f, width, 0.0f, height, -1.0f, 1.0f); - - glMatrixMode(GL_MODELVIEW); - gGL.pushMatrix(); - glLoadIdentity(); - - gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); - - gl_rect_2d_simple( width, height ); - - glMatrixMode(GL_PROJECTION); - gGL.popMatrix(); - - glMatrixMode(GL_MODELVIEW); - gGL.popMatrix(); - } - - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - - bool has_skin_weights = false; - bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); - bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); - - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - if (!model->mSkinWeights.empty()) - { - has_skin_weights = true; - } - } - } - - if (has_skin_weights) - { //model has skin weights, enable view options for skin weights and joint positions - if (fmp) - { - fmp->enableViewOption("show_skin_weight"); - fmp->setViewOptionEnabled("show_joint_positions", skin_weight); - } - mFMP->childEnable("upload_skin"); - } - else - { - mFMP->childDisable("upload_skin"); - if (fmp) - { - mViewOption["show_skin_weight"] = false; - fmp->disableViewOption("show_skin_weight"); - fmp->disableViewOption("show_joint_positions"); - } - skin_weight = false; - } - - if (upload_skin && !has_skin_weights) - { //can't upload skin weights if model has no skin weights - mFMP->childSetValue("upload_skin", false); - upload_skin = false; - } - - if (!upload_skin && upload_joints) - { //can't upload joints if not uploading skin weights - mFMP->childSetValue("upload_joints", false); - upload_joints = false; - } - - mFMP->childSetEnabled("upload_joints", upload_skin); - - F32 explode = mFMP->childGetValue("physics_explode").asReal(); - - glClear(GL_DEPTH_BUFFER_BIT); - - LLRect preview_rect = mFMP->getChildView("preview_panel")->getRect(); - F32 aspect = (F32) preview_rect.getWidth()/preview_rect.getHeight(); - - LLViewerCamera::getInstance()->setAspect(aspect); - - LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); - - LLVector3 offset = mCameraOffset; - LLVector3 target_pos = mPreviewTarget+offset; - - F32 z_near = 0.001f; - F32 z_far = mCameraDistance+mPreviewScale.magVec()+mCameraOffset.magVec(); - - if (skin_weight) - { - target_pos = gAgentAvatarp->getPositionAgent(); - z_near = 0.01f; - z_far = 1024.f; - mCameraDistance = 16.f; - - //render avatar previews every frame - refresh(); - } - - glLoadIdentity(); - gPipeline.enableLightsPreview(); - - LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * - LLQuaternion(mCameraYaw, LLVector3::z_axis); - - LLQuaternion av_rot = camera_rot; - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest - - - LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); - - stop_glerror(); - - gGL.pushMatrix(); - const F32 BRIGHTNESS = 0.9f; - gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); - - LLGLEnable normalize(GL_NORMALIZE); - - if (!mBaseModel.empty() && mVertexBuffer[5].empty()) - { - genBuffers(-1, skin_weight); - //genBuffers(3); - //genLODs(); - } - - if (!mModel[mPreviewLOD].empty()) - { - bool regen = mVertexBuffer[mPreviewLOD].empty(); - if (!regen) - { - const std::vector >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second; - if (!vb_vec.empty()) - { - const LLVertexBuffer* buff = vb_vec[0]; - regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; - } - } - - if (regen) - { - genBuffers(mPreviewLOD, skin_weight); - } - - if (!skin_weight) - { - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[mPreviewLOD]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - glMultMatrixf((GLfloat*) mat.mMatrix); - - for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); - - if (textures) - { - glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); - if (i < instance.mMaterial.size() && instance.mMaterial[i].mDiffuseMap.notNull()) - { - gGL.getTexUnit(0)->bind(instance.mMaterial[i].mDiffuseMap, true); - if (instance.mMaterial[i].mDiffuseMap->getDiscardLevel() > -1) - { - mTextureSet.insert(instance.mMaterial[i].mDiffuseMap); - } - } - } - else - { - glColor4f(1,1,1,1); - } - - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - glColor3f(0.4f, 0.4f, 0.4f); - - if (edges) - { - glLineWidth(3.f); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); - } - } - gGL.popMatrix(); - } - - if (physics) - { - glClear(GL_DEPTH_BUFFER_BIT); - LLGLEnable blend(GL_BLEND); - gGL.blendFunc(LLRender::BF_ONE, LLRender::BF_ZERO); - - for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) - { - LLModelInstance& instance = *iter; - - LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - - if (!model) - { - continue; - } - - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; - - glMultMatrixf((GLfloat*) mat.mMatrix); - - - bool render_mesh = true; - - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); - - std::map, std::vector > >::iterator iter = - mPhysicsMesh.find(model); - if (iter != mPhysicsMesh.end()) - { //render hull instead of mesh - render_mesh = false; - for (U32 i = 0; i < iter->second.size(); ++i) - { - if (explode > 0.f) - { - gGL.pushMatrix(); - - LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters; - offset *= explode; - - gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); - } - - static std::vector hull_colors; - - if (i+1 >= hull_colors.size()) - { - hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 255)); - } - - LLVertexBuffer* buff = iter->second[i]; - if (buff) - { - buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL); - - glColor4ubv(hull_colors[i].mV); - buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts()); - } - - if (explode > 0.f) - { - gGL.popMatrix(); - } - } - } - } - - if (render_mesh) - { - if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) - { - genBuffers(LLModel::LOD_PHYSICS, false); - } - for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - - buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); - - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - glColor4f(0.4f, 0.4f, 0.0f, 0.4f); - - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - - glColor3f(1.f, 1.f, 0.f); - - glLineWidth(3.f); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); - } - } - - gGL.popMatrix(); - } - - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - } - else - { - LLVOAvatarSelf* avatar = gAgentAvatarp; - target_pos = avatar->getPositionAgent(); - - LLViewerCamera::getInstance()->setOriginAndLookAt( - target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera - LLVector3::z_axis, // up - target_pos); // point of interest - - if (joint_positions) - { - avatar->renderCollisionVolumes(); - } - - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - - if (!model->mSkinWeights.empty()) - { - for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - const LLVolumeFace& face = model->getVolumeFace(i); - - LLStrider position; - buffer->getVertexStrider(position); - - LLStrider weight; - buffer->getWeight4Strider(weight); - - //quick 'n dirty software vertex skinning - - //build matrix palette - LLMatrix4 mat[64]; - for (U32 j = 0; j < model->mJointList.size(); ++j) - { - LLJoint* joint = avatar->getJoint(model->mJointList[j]); - if (joint) - { - mat[j] = model->mInvBindMatrix[j]; - mat[j] *= joint->getWorldMatrix(); - } - } - - for (U32 j = 0; j < buffer->getRequestedVerts(); ++j) - { - LLMatrix4 final_mat; - final_mat.mMatrix[0][0] = final_mat.mMatrix[1][1] = final_mat.mMatrix[2][2] = final_mat.mMatrix[3][3] = 0.f; - - LLVector4 wght; - S32 idx[4]; - - F32 scale = 0.f; - for (U32 k = 0; k < 4; k++) - { - F32 w = weight[j].mV[k]; - - idx[k] = (S32) floorf(w); - wght.mV[k] = w - floorf(w); - scale += wght.mV[k]; - } - - wght *= 1.f/scale; - - for (U32 k = 0; k < 4; k++) - { - F32* src = (F32*) mat[idx[k]].mMatrix; - F32* dst = (F32*) final_mat.mMatrix; - - F32 w = wght.mV[k]; - - for (U32 l = 0; l < 16; l++) - { - dst[l] += src[l]*w; - } - } - - //VECTORIZE THIS - LLVector3 v(face.mPositions[j].getF32ptr()); - - v = v * model->mBindShapeMatrix; - v = v * final_mat; - - position[j] = v; - } - - buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); - glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - glColor3f(0.4f, 0.4f, 0.4f); - - if (edges) - { - glLineWidth(3.f); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); - } - } - } - } - } - } - } - - gGL.popMatrix(); - - return TRUE; -} - -//----------------------------------------------------------------------------- -// refresh() -//----------------------------------------------------------------------------- -void LLModelPreview::refresh() -{ - mNeedsUpdate = TRUE; -} - -//----------------------------------------------------------------------------- -// rotate() -//----------------------------------------------------------------------------- -void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) -{ - mCameraYaw = mCameraYaw + yaw_radians; - - mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); -} - -//----------------------------------------------------------------------------- -// zoom() -//----------------------------------------------------------------------------- -void LLModelPreview::zoom(F32 zoom_amt) -{ - F32 new_zoom = mCameraZoom+zoom_amt; - - mCameraZoom = llclamp(new_zoom, 1.f, 10.f); -} - -void LLModelPreview::pan(F32 right, F32 up) -{ - mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); - mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); -} - -void LLModelPreview::setPreviewLOD(S32 lod) -{ - lod = llclamp(lod, 0, (S32) LLModel::LOD_HIGH); - - if (lod != mPreviewLOD) - { - mPreviewLOD = lod; - - LLComboBox* combo_box = mFMP->getChild("preview_lod_combo"); - combo_box->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order - mFMP->childSetTextArg("lod_table_footer", "[DETAIL]", mFMP->getString(lod_name[mPreviewLOD])); - mFMP->childSetText("lod_file", mLODFile[mPreviewLOD]); - - LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); - LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); - - for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i) - { - const LLColor4& color = (i == lod) ? highlight_color : normal_color; - - mFMP->childSetColor(lod_status_name[i], color); - mFMP->childSetColor(lod_label_name[i], color); - mFMP->childSetColor(lod_triangles_name[i], color); - mFMP->childSetColor(lod_vertices_name[i], color); - } - - LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; - if (fmp) - { - LLRadioGroup* radio = fmp->getChild("lod_file_or_limit"); - radio->selectNthItem(fmp->mLODMode[mPreviewLOD]); - } - } - refresh(); - updateStatusMessages(); -} - -//static -void LLFloaterModelPreview::onBrowseLOD(void* data) -{ - assert_main_thread(); - - LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; - mp->loadModel(mp->mModelPreview->mPreviewLOD); -} - -//static -void LLFloaterModelPreview::onUpload(void* user_data) -{ - assert_main_thread(); - - LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; - - mp->mModelPreview->rebuildUploadData(); - - gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale, - mp->childGetValue("upload_textures").asBoolean(), mp->childGetValue("upload_skin"), mp->childGetValue("upload_joints")); - - mp->closeFloater(false); -} - - -//static -void LLFloaterModelPreview::onClearMaterials(void* user_data) -{ - LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; - mp->mModelPreview->clearMaterials(); -} - -//static -void LLFloaterModelPreview::refresh(LLUICtrl* ctrl, void* user_data) -{ - sInstance->mModelPreview->mDirty = true; -} - -void LLFloaterModelPreview::updateResourceCost() -{ - U32 cost = mModelPreview->mResourceCost; - childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",cost)); -} - -//static -void LLModelPreview::textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ) -{ - LLModelPreview* preview = (LLModelPreview*) userdata; - preview->refresh(); -} - -LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LLModel* mdl) -{ - mStage = stage; - mContinue = 1; - mModel = mdl; - mDecompID = &mdl->mDecompID; - mParams = sInstance->mDecompParams; - - //copy out positions and indices - if (mdl) - { - U16 index_offset = 0; - - mPositions.clear(); - mIndices.clear(); - - //queue up vertex positions and indices - for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) - { - const LLVolumeFace& face = mdl->getVolumeFace(i); - if (mPositions.size() + face.mNumVertices > 65535) - { - continue; - } - - for (U32 j = 0; j < face.mNumVertices; ++j) - { - mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr())); - } - - for (U32 j = 0; j < face.mNumIndices; ++j) - { - mIndices.push_back(face.mIndices[j]+index_offset); - } - - index_offset += face.mNumVertices; - } - } -} - -void LLFloaterModelPreview::setStatusMessage(const std::string& msg) -{ - LLMutexLock lock(mStatusLock); - mStatusMessage = msg; -} - -S32 LLFloaterModelPreview::DecompRequest::statusCallback(const char* status, S32 p1, S32 p2) -{ - setStatusMessage(llformat("%s: %d/%d", status, p1, p2)); - if (LLFloaterModelPreview::sInstance) - { - LLFloaterModelPreview::sInstance->setStatusMessage(mStatusMessage); - } - - return mContinue; -} - -void LLFloaterModelPreview::DecompRequest::completed() -{ //called from the main thread - mModel->setConvexHullDecomposition(mHull); - - if (sInstance) - { - if (sInstance->mModelPreview) - { - sInstance->mModelPreview->mPhysicsMesh[mModel] = mHullMesh; - sInstance->mModelPreview->mDirty = true; - LLFloaterModelPreview::sInstance->mModelPreview->refresh(); - } - - sInstance->mCurRequest.erase(this); - } -} +/** + * @file llfloatermodelpreview.cpp + * @brief LLFloaterModelPreview class implementation + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "dae.h" +//#include "dom.h" +#include "dom/domAsset.h" +#include "dom/domBind_material.h" +#include "dom/domCOLLADA.h" +#include "dom/domConstants.h" +#include "dom/domController.h" +#include "dom/domEffect.h" +#include "dom/domGeometry.h" +#include "dom/domInstance_geometry.h" +#include "dom/domInstance_material.h" +#include "dom/domInstance_node.h" +#include "dom/domInstance_effect.h" +#include "dom/domMaterial.h" +#include "dom/domMatrix.h" +#include "dom/domNode.h" +#include "dom/domProfile_COMMON.h" +#include "dom/domRotate.h" +#include "dom/domScale.h" +#include "dom/domTranslate.h" +#include "dom/domVisual_scene.h" + +#include "llfloatermodelpreview.h" + +#include "llfilepicker.h" +#include "llimagebmp.h" +#include "llimagetga.h" +#include "llimagejpeg.h" +#include "llimagepng.h" + +#include "llagent.h" +#include "llbutton.h" +#include "llcombobox.h" +#include "lldatapacker.h" +#include "lldrawable.h" +#include "lldrawpoolavatar.h" +#include "llrender.h" +#include "llface.h" +#include "lleconomy.h" +#include "llfocusmgr.h" +#include "llfloaterperms.h" +#include "lliconctrl.h" +#include "llmatrix4a.h" +#include "llmenubutton.h" +#include "llmeshrepository.h" +#include "llsdutil_math.h" +#include "lltextbox.h" +#include "lltoolmgr.h" +#include "llui.h" +#include "llvector4a.h" +#include "llviewercamera.h" +#include "llviewerwindow.h" +#include "llvoavatar.h" +#include "llvoavatarself.h" +#include "pipeline.h" +#include "lluictrlfactory.h" +#include "llviewermenu.h" +#include "llviewermenufile.h" +#include "llviewerregion.h" +#include "llviewertexturelist.h" +#include "llstring.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llradiogroup.h" +#include "llsliderctrl.h" +#include "llspinctrl.h" +#include "lltoggleablemenu.h" +#include "llvfile.h" +#include "llvfs.h" +#include "llcallbacklist.h" + +#include "glod/glod.h" + +//static +S32 LLFloaterModelPreview::sUploadAmount = 10; +LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL; + +const S32 PREVIEW_BORDER_WIDTH = 2; +const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; +const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; +const S32 PREF_BUTTON_HEIGHT = 16 + 7 + 16; +const S32 PREVIEW_TEXTURE_HEIGHT = 300; + +void drawBoxOutline(const LLVector3& pos, const LLVector3& size); + + +std::string lod_name[NUM_LOD+1] = +{ + "lowest", + "low", + "medium", + "high", + "I went off the end of the lod_name array. Me so smart." +}; + +std::string lod_triangles_name[NUM_LOD+1] = +{ + "lowest_triangles", + "low_triangles", + "medium_triangles", + "high_triangles", + "I went off the end of the lod_triangles_name array. Me so smart." +}; + +std::string lod_vertices_name[NUM_LOD+1] = +{ + "lowest_vertices", + "low_vertices", + "medium_vertices", + "high_vertices", + "I went off the end of the lod_vertices_name array. Me so smart." +}; + +std::string lod_status_name[NUM_LOD+1] = +{ + "lowest_status", + "low_status", + "medium_status", + "high_status", + "I went off the end of the lod_status_name array. Me so smart." +}; + +std::string lod_icon_name[NUM_LOD+1] = +{ + "status_icon_lowest", + "status_icon_low", + "status_icon_medium", + "status_icon_high", + "I went off the end of the lod_status_name array. Me so smart." +}; + +std::string lod_status_image[NUM_LOD+1] = +{ + "ModelImport_Status_Good", + "ModelImport_Status_Warning", + "ModelImport_Status_Error", + "I went off the end of the lod_status_image array. Me so smart." +}; + +std::string lod_label_name[NUM_LOD+1] = +{ + "lowest_label", + "low_label", + "medium_label", + "high_label", + "I went off the end of the lod_label_name array. Me so smart." +}; + + +bool validate_face(const LLVolumeFace& face) +{ + for (U32 i = 0; i < face.mNumIndices; ++i) + { + if (face.mIndices[i] >= face.mNumVertices) + { + llwarns << "Face has invalid index." << llendl; + return false; + } + } + + return true; +} + +bool validate_model(const LLModel* mdl) +{ + if (mdl->getNumVolumeFaces() == 0) + { + llwarns << "Model has no faces!" << llendl; + return false; + } + + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + if (mdl->getVolumeFace(i).mNumVertices == 0) + { + llwarns << "Face has no vertices." << llendl; + return false; + } + + if (mdl->getVolumeFace(i).mNumIndices == 0) + { + llwarns << "Face has no indices." << llendl; + return false; + } + + if (!validate_face(mdl->getVolumeFace(i))) + { + return false; + } + } + + return true; +} + +BOOL stop_gloderror() +{ + GLuint error = glodGetError(); + + if (error != GLOD_NO_ERROR) + { + llwarns << "GLOD error detected, cannot generate LOD: " << std::hex << error << llendl; + return TRUE; + } + + return FALSE; +} + + +LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod) + : LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA) + { + mMP = mp; + mLOD = lod; + } + +void LLMeshFilePicker::notify(const std::string& filename) +{ + mMP->loadModel(mFile, mLOD); +} + + +//----------------------------------------------------------------------------- +// LLFloaterModelPreview() +//----------------------------------------------------------------------------- +LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) : +LLFloater(key) +{ + sInstance = this; + mLastMouseX = 0; + mLastMouseY = 0; + mGLName = 0; + mStatusLock = new LLMutex(NULL); + + mLODMode[LLModel::LOD_HIGH] = 0; + for (U32 i = 0; i < LLModel::LOD_HIGH; i++) + { + mLODMode[i] = 1; + } +} + +//----------------------------------------------------------------------------- +// postBuild() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::postBuild() +{ + if (!LLFloater::postBuild()) + { + return FALSE; + } + + + + + + + childSetAction("lod_browse", onBrowseLOD, this); + + childSetCommitCallback("crease_angle", onGenerateNormalsCommit, this); + childSetCommitCallback("generate_normals", onGenerateNormalsCommit, this); + + childSetCommitCallback("lod_generate", onAutoFillCommit, this); + + childSetCommitCallback("lod_mode", onLODParamCommit, this); + childSetCommitCallback("lod_error_threshold", onLODParamCommit, this); + childSetCommitCallback("lod_triangle_limit", onLODParamCommit, this); + childSetCommitCallback("build_operator", onLODParamCommit, this); + childSetCommitCallback("queue_mode", onLODParamCommit, this); + childSetCommitCallback("border_mode", onLODParamCommit, this); + childSetCommitCallback("share_tolerance", onLODParamCommit, this); + + childSetTextArg("status", "[STATUS]", getString("status_idle")); + + //childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount)); + childSetAction("ok_btn", onUpload, this); + childDisable("ok_btn"); + + childSetAction("clear_materials", onClearMaterials, this); + + childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this); + + childSetCommitCallback("upload_skin", onUploadSkinCommit, this); + childSetCommitCallback("upload_joints", onUploadJointsCommit, this); + + childSetCommitCallback("import_scale", onImportScaleCommit, this); + + childSetCommitCallback("lod_file_or_limit", refresh, this); + childSetCommitCallback("physics_load_radio", refresh, this); + //childSetCommitCallback("physics_optimize", refresh, this); + //childSetCommitCallback("physics_use_hull", refresh, this); + + childDisable("upload_skin"); + childDisable("upload_joints"); + childDisable("ok_btn"); + + mViewOptionMenuButton = getChild("options_gear_btn"); + + mCommitCallbackRegistrar.add("ModelImport.ViewOption.Action", boost::bind(&LLFloaterModelPreview::onViewOptionChecked, this, _2)); + mEnableCallbackRegistrar.add("ModelImport.ViewOption.Check", boost::bind(&LLFloaterModelPreview::isViewOptionChecked, this, _2)); + mEnableCallbackRegistrar.add("ModelImport.ViewOption.Enabled", boost::bind(&LLFloaterModelPreview::isViewOptionEnabled, this, _2)); + + + + mViewOptionMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_model_import_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mViewOptionMenuButton->setMenu(mViewOptionMenu, LLMenuButton::MP_BOTTOM_LEFT); + + initDecompControls(); + + LLView* preview_panel = getChild("preview_panel"); + + mPreviewRect = preview_panel->getRect(); + + mModelPreview = new LLModelPreview(512, 512, this); + mModelPreview->setPreviewTarget(16.f); + mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelPreview::setDetails, this, _1, _2, _3, _4, _5)); + + //set callbacks for left click on line editor rows + for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) + { + LLTextBox* text = getChild(lod_label_name[i]); + if (text) + { + text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); + } + + text = getChild(lod_triangles_name[i]); + if (text) + { + text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); + } + + text = getChild(lod_vertices_name[i]); + if (text) + { + text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); + } + + text = getChild(lod_status_name[i]); + if (text) + { + text->setMouseDownCallback(boost::bind(&LLModelPreview::setPreviewLOD, mModelPreview, i)); + } + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// LLFloaterModelPreview() +//----------------------------------------------------------------------------- +LLFloaterModelPreview::~LLFloaterModelPreview() +{ + sInstance = NULL; + + if ( mModelPreview->containsRiggedAsset() ) + { + gAgentAvatarp->resetJointPositions(); + } + + delete mModelPreview; + + if (mGLName) + { + LLImageGL::deleteTextures(1, &mGLName ); + } + + delete mStatusLock; + mStatusLock = NULL; +} + +void LLFloaterModelPreview::onViewOptionChecked(const LLSD& userdata) +{ + if (mModelPreview) + { + mModelPreview->mViewOption[userdata.asString()] = !mModelPreview->mViewOption[userdata.asString()]; + + mModelPreview->refresh(); + } +} + +bool LLFloaterModelPreview::isViewOptionChecked(const LLSD& userdata) +{ + if (mModelPreview) + { + return mModelPreview->mViewOption[userdata.asString()]; + } + + return false; +} + +bool LLFloaterModelPreview::isViewOptionEnabled(const LLSD& userdata) +{ + return !mViewOptionDisabled[userdata.asString()]; +} + +void LLFloaterModelPreview::setViewOptionEnabled(const std::string& option, bool enabled) +{ + mViewOptionDisabled[option] = !enabled; +} + +void LLFloaterModelPreview::enableViewOption(const std::string& option) +{ + setViewOptionEnabled(option, true); +} + +void LLFloaterModelPreview::disableViewOption(const std::string& option) +{ + setViewOptionEnabled(option, false); +} + +void LLFloaterModelPreview::loadModel(S32 lod) +{ + mModelPreview->mLoading = true; + + (new LLMeshFilePicker(mModelPreview, lod))->getFile(); +} + +//static +void LLFloaterModelPreview::onImportScaleCommit(LLUICtrl*,void* userdata) +{ + LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; + + if (!fp->mModelPreview) + { + return; + } + + fp->mModelPreview->calcResourceCost(); + fp->mModelPreview->refresh(); +} + +//static +void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata) +{ + LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; + + if (!fp->mModelPreview) + { + return; + } + + fp->mModelPreview->refresh(); +} + +//static +void LLFloaterModelPreview::onUploadSkinCommit(LLUICtrl*,void* userdata) +{ + LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; + + if (!fp->mModelPreview) + { + return; + } + + fp->mModelPreview->refresh(); + fp->mModelPreview->resetPreviewTarget(); + fp->mModelPreview->clearBuffers(); +} + +//static +void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; + + if (!fp->mModelPreview) + { + return; + } + + S32 which_mode = 0; + + LLComboBox* combo = (LLComboBox*) ctrl; + + which_mode = (NUM_LOD-1)-combo->getFirstSelectedIndex(); // combo box list of lods is in reverse order + + fp->mModelPreview->setPreviewLOD(which_mode); +} + +//static +void LLFloaterModelPreview::onGenerateNormalsCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; + + fp->mModelPreview->generateNormals(); +} + +//static +void LLFloaterModelPreview::onExplodeCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview* fp = LLFloaterModelPreview::sInstance; + + fp->mModelPreview->refresh(); +} + +//static +void LLFloaterModelPreview::onAutoFillCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; + + fp->mModelPreview->genLODs(); +} + +//static +void LLFloaterModelPreview::onLODParamCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; + fp->mModelPreview->genLODs(fp->mModelPreview->mPreviewLOD); + fp->mModelPreview->updateStatusMessages(); + fp->mModelPreview->refresh(); +} + + +//----------------------------------------------------------------------------- +// draw() +//----------------------------------------------------------------------------- +void LLFloaterModelPreview::draw() +{ + LLFloater::draw(); + LLRect r = getRect(); + + mModelPreview->update(); + + if (!mModelPreview->mLoading) + { + childSetTextArg("status", "[STATUS]", getString("status_idle")); + } + + childSetTextArg("prim_cost", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost)); + childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size())); + + if (!mCurRequest.empty()) + { + LLMutexLock lock(mStatusLock); + childSetTextArg("status", "[STATUS]", mStatusMessage); + } + + U32 resource_cost = mModelPreview->mResourceCost*10; + + if (childGetValue("upload_textures").asBoolean()) + { + resource_cost += mModelPreview->mTextureSet.size()*10; + } + + childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", resource_cost)); + + if (mModelPreview) + { + gGL.color3f(1.f, 1.f, 1.f); + + gGL.getTexUnit(0)->bind(mModelPreview); + + + LLView* preview_panel = getChild("preview_panel"); + + LLRect rect = preview_panel->getRect(); + if (rect != mPreviewRect) + { + mModelPreview->refresh(); + mPreviewRect = preview_panel->getRect(); + } + + gGL.begin( LLRender::QUADS ); + { + gGL.texCoord2f(0.f, 1.f); + gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex2i(mPreviewRect.mRight, mPreviewRect.mBottom); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex2i(mPreviewRect.mRight, mPreviewRect.mTop); + } + gGL.end(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } +} + +//----------------------------------------------------------------------------- +// handleMouseDown() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (mPreviewRect.pointInRect(x, y)) + { + bringToFront( x, y ); + gFocusMgr.setMouseCapture(this); + gViewerWindow->hideCursor(); + mLastMouseX = x; + mLastMouseY = y; + return TRUE; + } + + return LLFloater::handleMouseDown(x, y, mask); +} + +//----------------------------------------------------------------------------- +// handleMouseUp() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::handleMouseUp(S32 x, S32 y, MASK mask) +{ + gFocusMgr.setMouseCapture(FALSE); + gViewerWindow->showCursor(); + return LLFloater::handleMouseUp(x, y, mask); +} + +//----------------------------------------------------------------------------- +// handleHover() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::handleHover (S32 x, S32 y, MASK mask) +{ + MASK local_mask = mask & ~MASK_ALT; + + if (mModelPreview && hasMouseCapture()) + { + if (local_mask == MASK_PAN) + { + // pan here + mModelPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f); + } + else if (local_mask == MASK_ORBIT) + { + F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; + F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f; + + mModelPreview->rotate(yaw_radians, pitch_radians); + } + else + { + + F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; + F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f; + + mModelPreview->rotate(yaw_radians, 0.f); + mModelPreview->zoom(zoom_amt); + } + + + mModelPreview->refresh(); + + LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY); + } + + if (!mPreviewRect.pointInRect(x, y) || !mModelPreview) + { + return LLFloater::handleHover(x, y, mask); + } + else if (local_mask == MASK_ORBIT) + { + gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA); + } + else if (local_mask == MASK_PAN) + { + gViewerWindow->setCursor(UI_CURSOR_TOOLPAN); + } + else + { + gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN); + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// handleScrollWheel() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if (mPreviewRect.pointInRect(x, y) && mModelPreview) + { + mModelPreview->zoom((F32)clicks * -0.2f); + mModelPreview->refresh(); + } + + return TRUE; +} + +//static +void LLFloaterModelPreview::onPhysicsParamCommit(LLUICtrl* ctrl, void* data) +{ + if (LLConvexDecomposition::getInstance() == NULL) + { + llinfos << "convex decomposition tool is a stub on this platform. cannot get decomp." << llendl; + return; + } + + if (sInstance) + { + LLCDParam* param = (LLCDParam*) data; + std::string name(param->mName); + sInstance->mDecompParams[name] = ctrl->getValue(); + + if (name == "Simplify Method") + { + if (ctrl->getValue().asInteger() == 0) + { + sInstance->childSetVisible("Retain%", true); + sInstance->childSetVisible("Detail Scale", false); + } + else + { + sInstance->childSetVisible("Retain%", false); + sInstance->childSetVisible("Detail Scale", true); + } + } + } +} + +//static +void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data) +{ + LLCDStageData* stage = (LLCDStageData*) data; + + if (sInstance) + { + if (!sInstance->mCurRequest.empty()) + { + llinfos << "Decomposition request still pending." << llendl; + return; + } + + if (sInstance->mModelPreview) + { + for (S32 i = 0; i < sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS].size(); ++i) + { + LLModel* mdl = sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS][i]; + DecompRequest* request = new DecompRequest(stage->mName, mdl); + sInstance->mCurRequest.insert(request); + gMeshRepo.mDecompThread->submitRequest(request); + } + } + } +} + +//static +void LLFloaterModelPreview::onPhysicsBrowse(LLUICtrl* ctrl, void* userdata) +{ + sInstance->loadModel(LLModel::LOD_PHYSICS); +} + +//static +void LLFloaterModelPreview::onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata) +{ + S32 which_mode = 3; + LLCtrlSelectionInterface* iface = sInstance->childGetSelectionInterface("physics_lod_combo"); + if (iface) + { + which_mode = iface->getFirstSelectedIndex(); + } + + sInstance->mModelPreview->setPhysicsFromLOD(which_mode); +} + +//static +void LLFloaterModelPreview::onPhysicsStageCancel(LLUICtrl* ctrl, void*data) +{ + if (sInstance) + { + for (std::set >::iterator iter = sInstance->mCurRequest.begin(); + iter != sInstance->mCurRequest.end(); ++iter) + { + DecompRequest* req = *iter; + req->mContinue = 0; + } + } +} + +void LLFloaterModelPreview::initDecompControls() +{ + LLSD key; + + childSetCommitCallback("cancel_btn", onPhysicsStageCancel, NULL); + childSetCommitCallback("physics_lod_combo", onPhysicsUseLOD, NULL); + childSetCommitCallback("physics_browse", onPhysicsBrowse, NULL); + + static const LLCDStageData* stage = NULL; + static S32 stage_count = 0; + + if (!stage && LLConvexDecomposition::getInstance() != NULL) + { + stage_count = LLConvexDecomposition::getInstance()->getStages(&stage); + } + + static const LLCDParam* param = NULL; + static S32 param_count = 0; + if (!param && LLConvexDecomposition::getInstance() != NULL) + { + param_count = LLConvexDecomposition::getInstance()->getParameters(¶m); + } + + for (S32 j = stage_count-1; j >= 0; --j) + { + LLButton* button = getChild(stage[j].mName); + if (button) + { + button->setCommitCallback(onPhysicsStageExecute, (void*) &stage[j]); + } + + gMeshRepo.mDecompThread->mStageID[stage[j].mName] = j; + // protected against stub by stage_count being 0 for stub above + LLConvexDecomposition::getInstance()->registerCallback(j, LLPhysicsDecomp::llcdCallback); + + //llinfos << "Physics decomp stage " << stage[j].mName << " (" << j << ") parameters:" << llendl; + //llinfos << "------------------------------------" << llendl; + + for (S32 i = 0; i < param_count; ++i) + { + if (param[i].mStage != j) + { + continue; + } + + std::string name(param[i].mName ? param[i].mName : ""); + std::string description(param[i].mDescription ? param[i].mDescription : ""); + + std::string type = "unknown"; + + llinfos << name << " - " << description << llendl; + + if (param[i].mType == LLCDParam::LLCD_FLOAT) + { + mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mFloat); + //llinfos << "Type: float, Default: " << param[i].mDefault.mFloat << llendl; + + LLSliderCtrl* slider = getChild(name); + if (slider) + { + slider->setMinValue(param[i].mDetails.mRange.mLow.mFloat); + slider->setMaxValue(param[i].mDetails.mRange.mHigh.mFloat); + slider->setIncrement(param[i].mDetails.mRange.mDelta.mFloat); + slider->setValue(param[i].mDefault.mFloat); + slider->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); + } + } + else if (param[i].mType == LLCDParam::LLCD_INTEGER) + { + mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue); + //llinfos << "Type: integer, Default: " << param[i].mDefault.mIntOrEnumValue << llendl; + + LLSliderCtrl* slider = getChild(name); + if (slider) + { + slider->setMinValue(param[i].mDetails.mRange.mLow.mIntOrEnumValue); + slider->setMaxValue(param[i].mDetails.mRange.mHigh.mIntOrEnumValue); + slider->setIncrement(param[i].mDetails.mRange.mDelta.mIntOrEnumValue); + slider->setValue(param[i].mDefault.mIntOrEnumValue); + slider->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); + } + } + else if (param[i].mType == LLCDParam::LLCD_BOOLEAN) + { + mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mBool); + //llinfos << "Type: boolean, Default: " << (param[i].mDefault.mBool ? "True" : "False") << llendl; + + LLCheckBoxCtrl* check_box = getChild(name); + if (check_box) + { + check_box->setValue(param[i].mDefault.mBool); + check_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); + } + } + else if (param[i].mType == LLCDParam::LLCD_ENUM) + { + mDecompParams[param[i].mName] = LLSD(param[i].mDefault.mIntOrEnumValue); + //llinfos << "Type: enum, Default: " << param[i].mDefault.mIntOrEnumValue << llendl; + + { //plug into combo box + + //llinfos << "Accepted values: " << llendl; + LLComboBox* combo_box = getChild(name); + for (S32 k = 0; k < param[i].mDetails.mEnumValues.mNumEnums; ++k) + { + //llinfos << param[i].mDetails.mEnumValues.mEnumsArray[k].mValue + // << " - " << param[i].mDetails.mEnumValues.mEnumsArray[k].mName << llendl; + + combo_box->add(param[i].mDetails.mEnumValues.mEnumsArray[k].mName, + LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue)); + } + combo_box->setValue(param[i].mDefault.mIntOrEnumValue); + combo_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); + } + + //llinfos << "----" << llendl; + } + //llinfos << "-----------------------------" << llendl; + } + } + + childSetCommitCallback("physics_explode", LLFloaterModelPreview::onExplodeCommit, this); +} + +//----------------------------------------------------------------------------- +// onMouseCaptureLost() +//----------------------------------------------------------------------------- +// static +void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handler) +{ + gViewerWindow->showCursor(); +} + +//----------------------------------------------------------------------------- +// LLModelLoader +//----------------------------------------------------------------------------- +LLModelLoader::LLModelLoader(std::string filename, S32 lod, LLModelPreview* preview) +: LLThread("Model Loader"), mFilename(filename), mLod(lod), mPreview(preview), mState(STARTING), mFirstTransform(TRUE) +{ + mJointMap["mPelvis"] = "mPelvis"; + mJointMap["mTorso"] = "mTorso"; + mJointMap["mChest"] = "mChest"; + mJointMap["mNeck"] = "mNeck"; + mJointMap["mHead"] = "mHead"; + mJointMap["mSkull"] = "mSkull"; + mJointMap["mEyeRight"] = "mEyeRight"; + mJointMap["mEyeLeft"] = "mEyeLeft"; + mJointMap["mCollarLeft"] = "mCollarLeft"; + mJointMap["mShoulderLeft"] = "mShoulderLeft"; + mJointMap["mElbowLeft"] = "mElbowLeft"; + mJointMap["mWristLeft"] = "mWristLeft"; + mJointMap["mCollarRight"] = "mCollarRight"; + mJointMap["mShoulderRight"] = "mShoulderRight"; + mJointMap["mElbowRight"] = "mElbowRight"; + mJointMap["mWristRight"] = "mWristRight"; + mJointMap["mHipRight"] = "mHipRight"; + mJointMap["mKneeRight"] = "mKneeRight"; + mJointMap["mAnkleRight"] = "mAnkleRight"; + mJointMap["mFootRight"] = "mFootRight"; + mJointMap["mToeRight"] = "mToeRight"; + mJointMap["mHipLeft"] = "mHipLeft"; + mJointMap["mKneeLeft"] = "mKneeLeft"; + mJointMap["mAnkleLeft"] = "mAnkleLeft"; + mJointMap["mFootLeft"] = "mFootLeft"; + mJointMap["mToeLeft"] = "mToeLeft"; + + mJointMap["avatar_mPelvis"] = "mPelvis"; + mJointMap["avatar_mTorso"] = "mTorso"; + mJointMap["avatar_mChest"] = "mChest"; + mJointMap["avatar_mNeck"] = "mNeck"; + mJointMap["avatar_mHead"] = "mHead"; + mJointMap["avatar_mSkull"] = "mSkull"; + mJointMap["avatar_mEyeRight"] = "mEyeRight"; + mJointMap["avatar_mEyeLeft"] = "mEyeLeft"; + mJointMap["avatar_mCollarLeft"] = "mCollarLeft"; + mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft"; + mJointMap["avatar_mElbowLeft"] = "mElbowLeft"; + mJointMap["avatar_mWristLeft"] = "mWristLeft"; + mJointMap["avatar_mCollarRight"] = "mCollarRight"; + mJointMap["avatar_mShoulderRight"] = "mShoulderRight"; + mJointMap["avatar_mElbowRight"] = "mElbowRight"; + mJointMap["avatar_mWristRight"] = "mWristRight"; + mJointMap["avatar_mHipRight"] = "mHipRight"; + mJointMap["avatar_mKneeRight"] = "mKneeRight"; + mJointMap["avatar_mAnkleRight"] = "mAnkleRight"; + mJointMap["avatar_mFootRight"] = "mFootRight"; + mJointMap["avatar_mToeRight"] = "mToeRight"; + mJointMap["avatar_mHipLeft"] = "mHipLeft"; + mJointMap["avatar_mKneeLeft"] = "mKneeLeft"; + mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft"; + mJointMap["avatar_mFootLeft"] = "mFootLeft"; + mJointMap["avatar_mToeLeft"] = "mToeLeft"; + + + mJointMap["hip"] = "mPelvis"; + mJointMap["abdomen"] = "mTorso"; + mJointMap["chest"] = "mChest"; + mJointMap["neck"] = "mNeck"; + mJointMap["head"] = "mHead"; + mJointMap["figureHair"] = "mSkull"; + mJointMap["lCollar"] = "mCollarLeft"; + mJointMap["lShldr"] = "mShoulderLeft"; + mJointMap["lForeArm"] = "mElbowLeft"; + mJointMap["lHand"] = "mWristLeft"; + mJointMap["rCollar"] = "mCollarRight"; + mJointMap["rShldr"] = "mShoulderRight"; + mJointMap["rForeArm"] = "mElbowRight"; + mJointMap["rHand"] = "mWristRight"; + mJointMap["rThigh"] = "mHipRight"; + mJointMap["rShin"] = "mKneeRight"; + mJointMap["rFoot"] = "mFootRight"; + mJointMap["lThigh"] = "mHipLeft"; + mJointMap["lShin"] = "mKneeLeft"; + mJointMap["lFoot"] = "mFootLeft"; +} + +void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform) +{ + LLVector4a box[] = + { + LLVector4a(-1, 1,-1), + LLVector4a(-1, 1, 1), + LLVector4a(-1,-1,-1), + LLVector4a(-1,-1, 1), + LLVector4a( 1, 1,-1), + LLVector4a( 1, 1, 1), + LLVector4a( 1,-1,-1), + LLVector4a( 1,-1, 1), + }; + + for (S32 j = 0; j < model->getNumVolumeFaces(); ++j) + { + const LLVolumeFace& face = model->getVolumeFace(j); + + LLVector4a center; + center.setAdd(face.mExtents[0], face.mExtents[1]); + center.mul(0.5f); + LLVector4a size; + size.setSub(face.mExtents[1],face.mExtents[0]); + size.mul(0.5f); + + for (U32 i = 0; i < 8; i++) + { + LLVector4a t; + t.setMul(size, box[i]); + t.add(center); + + LLVector4a v; + + mat.affineTransform(t, v); + + if (first_transform) + { + first_transform = FALSE; + min = max = v; + } + else + { + update_min_max(min, max, v); + } + } + } +} + +void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform) +{ + LLVector4a mina, maxa; + LLMatrix4a mata; + + mata.loadu(mat); + mina.load3(min.mV); + maxa.load3(max.mV); + + stretch_extents(model, mata, mina, maxa, first_transform); + + min.set(mina.getF32ptr()); + max.set(maxa.getF32ptr()); +} + +void LLModelLoader::run() +{ + DAE dae; + domCOLLADA* dom = dae.open(mFilename); + + if (dom) + { + daeDatabase* db = dae.getDatabase(); + + daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH); + + daeDocument* doc = dae.getDoc(mFilename); + if (!doc) + { + llwarns << "can't find internal doc" << llendl; + return; + } + + daeElement* root = doc->getDomRoot(); + if (!root) + { + llwarns << "document has no root" << llendl; + return; + } + + //get unit scale + mTransform.setIdentity(); + + domAsset::domUnit* unit = daeSafeCast(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID()))); + + if (unit) + { + F32 meter = unit->getMeter(); + mTransform.mMatrix[0][0] = meter; + mTransform.mMatrix[1][1] = meter; + mTransform.mMatrix[2][2] = meter; + } + + //get up axis rotation + LLMatrix4 rotation; + + domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP + domAsset::domUp_axis* up_axis = + daeSafeCast(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID()))); + + if (up_axis) + { + up = up_axis->getValue(); + } + + if (up == UPAXISTYPE_X_UP) + { + rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f); + } + else if (up == UPAXISTYPE_Y_UP) + { + rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); + } + + rotation *= mTransform; + mTransform = rotation; + + + for (daeInt idx = 0; idx < count; ++idx) + { //build map of domEntities to LLModel + domMesh* mesh = NULL; + db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH); + + if (mesh) + { + LLPointer model = LLModel::loadModelFromDomMesh(mesh); + + if (model.notNull() && validate_model(model)) + { + mModelList.push_back(model); + mModel[mesh] = model; + } + } + } + + count = db->getElementCount(NULL, COLLADA_TYPE_SKIN); + for (daeInt idx = 0; idx < count; ++idx) + { //add skinned meshes as instances + domSkin* skin = NULL; + db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN); + + if (skin) + { + domGeometry* geom = daeSafeCast(skin->getSource().getElement()); + + if (geom) + { + domMesh* mesh = geom->getMesh(); + if (mesh) + { + LLModel* model = mModel[mesh]; + if (model) + { + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + + LLMatrix4 normalized_transformation; + normalized_transformation.setTranslation(mesh_translation_vector); + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= normalized_transformation; + normalized_transformation = mesh_scale; + + glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix); + inv_mat = inv_mat.inverse(); + LLMatrix4 inverse_normalized_transformation(inv_mat.m); + + domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix(); + + if (bind_mat) + { //get bind shape matrix + domFloat4x4& dom_value = bind_mat->getValue(); + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + model->mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4]; + } + } + + LLMatrix4 trans = normalized_transformation; + trans *= model->mBindShapeMatrix; + model->mBindShapeMatrix = trans; + + } + + + //The joint transfom map that we'll populate below + std::map jointTransforms; + jointTransforms.clear(); + + //Some collada setup for accessing the skeleton + daeElement* pElement = 0; + dae.getDatabase()->getElement( &pElement, 0, 0, "skeleton" ); + + //Try to get at the skeletal instance controller + domInstance_controller::domSkeleton* pSkeleton = daeSafeCast( pElement ); + bool missingSkeletonOrScene = false; + + //If no skeleton, do a breadth-first search to get at specific joints + if ( !pSkeleton ) + { + daeElement* pScene = root->getDescendant("visual_scene"); + if ( !pScene ) + { + llwarns<<"No visual scene - unable to parse bone offsets "< > children = pScene->getChildren(); + S32 childCount = children.getCount(); + + //Process any children that are joints + //Not all children are joints, some code be ambient lights, cameras, geometry etc.. + for (S32 i = 0; i < childCount; ++i) + { + domNode* pNode = daeSafeCast(children[i]); + if ( isNodeAJoint( pNode ) ) + { + processJointNode( pNode, jointTransforms ); + } + } + } + } + else + //Has Skeleton + { + //Get the root node of the skeleton + daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement(); + if ( pSkeletonRootNode ) + { + //Once we have the root node - start acccessing it's joint components + const int jointCnt = mJointMap.size(); + std::map :: const_iterator jointIt = mJointMap.begin(); + + //Loop over all the possible joints within the .dae - using the allowed joint list in the ctor. + for ( int i=0; i( resolver.getElement() ); + if ( pJoint ) + { + //Pull out the translate id and store it in the jointTranslations map + daeSIDResolver jointResolver( pJoint, "./translate" ); + domTranslate* pTranslate = daeSafeCast( jointResolver.getElement() ); + + LLMatrix4 workingTransform; + + //Translation via SID + if ( pTranslate ) + { + extractTranslation( pTranslate, workingTransform ); + } + else + { + //Translation via child from element + daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" ); + if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() ) + { + llwarns<< "The found element is not a translate node" < :: const_iterator jointIt = mJointMap.begin(); + for ( int i=0; igetJoint( lookingForJoint ); + if ( pJoint ) + { + pJoint->storeCurrentXform( jointTransform.getTranslation() ); + } + else + { + //Most likely an error in the asset. + llwarns<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << llendl; + } + //Reposition the avatars pelvis (avPos+offset) + //if ( lookingForJoint == "mPelvis" ) + //{ + // const LLVector3& pos = gAgentAvatarp->getCharacterPosition(); + // gAgentAvatarp->setPelvisOffset( true, jointTransform.getTranslation() ); + // gAgentAvatarp->setPosition( pos + jointTransform.getTranslation() ); + //} + } + } + } //missingSkeletonOrScene + + domSkin::domJoints* joints = skin->getJoints(); + + domInputLocal_Array& joint_input = joints->getInput_array(); + + for (size_t i = 0; i < joint_input.getCount(); ++i) + { + domInputLocal* input = joint_input.get(i); + xsNMTOKEN semantic = input->getSemantic(); + + if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0) + { //found joint source, fill model->mJointMap and model->mJointList + daeElement* elem = input->getSource().getElement(); + + domSource* source = daeSafeCast(elem); + if (source) + { + + + domName_array* names_source = source->getName_array(); + + if (names_source) + { + domListOfNames &names = names_source->getValue(); + + for (size_t j = 0; j < names.getCount(); ++j) + { + std::string name(names.get(j)); + if (mJointMap.find(name) != mJointMap.end()) + { + name = mJointMap[name]; + } + model->mJointList.push_back(name); + model->mJointMap[name] = j; + } + } + else + { + domIDREF_array* names_source = source->getIDREF_array(); + if (names_source) + { + xsIDREFS& names = names_source->getValue(); + + for (size_t j = 0; j < names.getCount(); ++j) + { + std::string name(names.get(j).getID()); + if (mJointMap.find(name) != mJointMap.end()) + { + name = mJointMap[name]; + } + model->mJointList.push_back(name); + model->mJointMap[name] = j; + } + } + } + } + } + else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0) + { //found inv_bind_matrix array, fill model->mInvBindMatrix + domSource* source = daeSafeCast(input->getSource().getElement()); + if (source) + { + domFloat_array* t = source->getFloat_array(); + if (t) + { + domListOfFloats& transform = t->getValue(); + S32 count = transform.getCount()/16; + + for (S32 k = 0; k < count; ++k) + { + LLMatrix4 mat; + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + mat.mMatrix[i][j] = transform[k*16 + i + j*4]; + } + } + + model->mInvBindMatrix.push_back(mat); + } + } + } + } + } + + //We need to construct the alternate bind matrix (which contains the new joint positions) + //in the same order as they were stored in the joint buffer. The joints associated + //with the skeleton are not stored in the same order as they are in the exported joint buffer. + //This remaps the skeletal joints to be in the same order as the joints stored in the model. + std::vector :: const_iterator jointIt = model->mJointList.begin(); + const int jointCnt = model->mJointList.size(); + for ( int i=0; imInvBindMatrix[i]; + newInverse.setTranslation( jointTransforms[lookingForJoint].getTranslation() ); + model->mAlternateBindMatrix.push_back( newInverse ); + } + else + { + llwarns<<"Possibly misnamed/missing joint [" <getVertices(); + if (verts) + { + domInputLocal_Array& inputs = verts->getInput_array(); + for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i) + { + if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0) + { + domSource* pos_source = daeSafeCast(inputs[i]->getSource().getElement()); + if (pos_source) + { + domFloat_array* pos_array = pos_source->getFloat_array(); + if (pos_array) + { + domListOfFloats& pos = pos_array->getValue(); + + for (size_t j = 0; j < pos.getCount(); j += 3) + { + if (pos.getCount() <= j+2) + { + llerrs << "WTF?" << llendl; + } + + LLVector3 v(pos[j], pos[j+1], pos[j+2]); + + //transform from COLLADA space to volume space + v = v * inverse_normalized_transformation; + + model->mPosition.push_back(v); + } + } + } + } + } + } + + //grab skin weights array + domSkin::domVertex_weights* weights = skin->getVertex_weights(); + if (weights) + { + domInputLocalOffset_Array& inputs = weights->getInput_array(); + domFloat_array* vertex_weights = NULL; + for (size_t i = 0; i < inputs.getCount(); ++i) + { + if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0) + { + domSource* weight_source = daeSafeCast(inputs[i]->getSource().getElement()); + if (weight_source) + { + vertex_weights = weight_source->getFloat_array(); + } + } + } + + if (vertex_weights) + { + domListOfFloats& w = vertex_weights->getValue(); + domListOfUInts& vcount = weights->getVcount()->getValue(); + domListOfInts& v = weights->getV()->getValue(); + + U32 c_idx = 0; + for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx) + { //for each vertex + daeUInt count = vcount[vc_idx]; + + //create list of weights that influence this vertex + LLModel::weight_list weight_list; + + for (daeUInt i = 0; i < count; ++i) + { //for each weight + daeInt joint_idx = v[c_idx++]; + daeInt weight_idx = v[c_idx++]; + + if (joint_idx == -1) + { + //ignore bindings to bind_shape_matrix + continue; + } + + F32 weight_value = w[weight_idx]; + + weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value)); + } + + //sort by joint weight + std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); + + std::vector wght; + + F32 total = 0.f; + + for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i) + { //take up to 4 most significant weights + if (weight_list[i].mWeight > 0.f) + { + wght.push_back( weight_list[i] ); + total += weight_list[i].mWeight; + } + } + + F32 scale = 1.f/total; + if (scale != 1.f) + { //normalize weights + for (U32 i = 0; i < wght.size(); ++i) + { + wght[i].mWeight *= scale; + } + } + + model->mSkinWeights[model->mPosition[vc_idx]] = wght; + } + + //add instance to scene for this model + + LLMatrix4 transformation = mTransform; + // adjust the transformation to compensate for mesh normalization + + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(mesh_translation_vector); + mesh_translation *= transformation; + transformation = mesh_translation; + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= transformation; + transformation = mesh_scale; + + std::vector materials; + materials.resize(model->getNumVolumeFaces()); + mScene[transformation].push_back(LLModelInstance(model, transformation, materials)); + stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); + } + } + } + } + } + } + } + + daeElement* scene = root->getDescendant("visual_scene"); + + if (!scene) + { + llwarns << "document has no visual_scene" << llendl; + setLoadState( ERROR_PARSING ); + return; + } + + setLoadState( DONE ); + + processElement(scene); + + doOnIdleOneTime(boost::bind(&LLModelPreview::loadModelCallback,mPreview,mLod)); + } +} + +bool LLModelLoader::isNodeAJoint( domNode* pNode ) +{ + if ( pNode->getName() == NULL) + { + return false; + } + + if ( mJointMap.find( pNode->getName() ) != mJointMap.end() ) + { + return true; + } + + return false; +} + +void LLModelLoader::extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ) +{ + domFloat3 jointTrans = pTranslate->getValue(); + LLVector3 singleJointTranslation( jointTrans[0], jointTrans[1], jointTrans[2] ); + transform.setTranslation( singleJointTranslation ); +} + +void LLModelLoader::extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ) +{ + domTranslate* pTranslateChild = dynamic_cast( pTranslateElement ); + domFloat3 translateChild = pTranslateChild->getValue(); + LLVector3 singleJointTranslation( translateChild[0], translateChild[1], translateChild[2] ); + transform.setTranslation( singleJointTranslation ); +} + +void LLModelLoader::processJointNode( domNode* pNode, std::map& jointTransforms ) +{ + if (pNode->getName() == NULL) + { + llwarns << "nameless node, can't process" << llendl; + return; + } + + //llwarns<<"ProcessJointNode# Node:" <getName()<( jointResolver.getElement() ); + + //Translation via SID was successful + if ( pTranslate ) + { + extractTranslation( pTranslate, workingTransform ); + } + else + { + //Translation via child from element + daeElement* pTranslateElement = getChildFromElement( pNode, "translate" ); + if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() ) + { + llwarns<< "The found element is not a translate node" <getName() ] = workingTransform; + + //2. handle the nodes children + + //Gather and handle the incoming nodes children + daeTArray< daeSmartRef > childOfChild = pNode->getChildren(); + S32 childOfChildCount = childOfChild.getCount(); + + for (S32 i = 0; i < childOfChildCount; ++i) + { + domNode* pChildNode = daeSafeCast( childOfChild[i] ); + if ( pChildNode ) + { + processJointNode( pChildNode, jointTransforms ); + } + } +} + +daeElement* LLModelLoader::getChildFromElement( daeElement* pElement, std::string const & name ) +{ + daeElement* pChildOfElement = pElement->getChild( name.c_str() ); + if ( pChildOfElement ) + { + return pChildOfElement; + } + llwarns<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << llendl; + return NULL; +} + +void LLModelLoader::processElement(daeElement* element) +{ + LLMatrix4 saved_transform = mTransform; + + domTranslate* translate = daeSafeCast(element); + if (translate) + { + domFloat3 dom_value = translate->getValue(); + + LLMatrix4 translation; + translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + + translation *= mTransform; + mTransform = translation; + } + + domRotate* rotate = daeSafeCast(element); + if (rotate) + { + domFloat4 dom_value = rotate->getValue(); + + LLMatrix4 rotation; + rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0)); + + rotation *= mTransform; + mTransform = rotation; + } + + domScale* scale = daeSafeCast(element); + if (scale) + { + domFloat3 dom_value = scale->getValue(); + + LLMatrix4 scaling; + scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + + scaling *= mTransform; + mTransform = scaling; + } + + domMatrix* matrix = daeSafeCast(element); + if (matrix) + { + domFloat4x4 dom_value = matrix->getValue(); + + LLMatrix4 matrix_transform; + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + matrix_transform.mMatrix[i][j] = dom_value[i + j*4]; + } + } + + matrix_transform *= mTransform; + mTransform = matrix_transform; + } + + domInstance_geometry* instance_geo = daeSafeCast(element); + if (instance_geo) + { + domGeometry* geo = daeSafeCast(instance_geo->getUrl().getElement()); + if (geo) + { + domMesh* mesh = daeSafeCast(geo->getDescendant(daeElement::matchType(domMesh::ID()))); + if (mesh) + { + LLModel* model = mModel[mesh]; + if (model) + { + LLMatrix4 transformation = mTransform; + + std::vector materials = getMaterials(model, instance_geo); + + // adjust the transformation to compensate for mesh normalization + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(mesh_translation_vector); + mesh_translation *= transformation; + transformation = mesh_translation; + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= transformation; + transformation = mesh_scale; + + mScene[transformation].push_back(LLModelInstance(model, transformation, materials)); + + stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); + } + } + } + } + + domInstance_node* instance_node = daeSafeCast(element); + if (instance_node) + { + daeElement* instance = instance_node->getUrl().getElement(); + if (instance) + { + processElement(instance); + } + } + + //process children + daeTArray< daeSmartRef > children = element->getChildren(); + for (S32 i = 0; i < children.getCount(); i++) + { + processElement(children[i]); + } + + domNode* node = daeSafeCast(element); + if (node) + { //this element was a node, restore transform before processiing siblings + mTransform = saved_transform; + } +} + +std::vector LLModelLoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo) +{ + std::vector materials; + for (int i = 0; i < model->mMaterialList.size(); i++) + { + LLImportMaterial import_material; + + domInstance_material* instance_mat = NULL; + + domBind_material::domTechnique_common* technique = + daeSafeCast(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); + + if (technique) + { + daeTArray< daeSmartRef > inst_materials = technique->getChildrenByType(); + for (int j = 0; j < inst_materials.getCount(); j++) + { + std::string symbol(inst_materials[j]->getSymbol()); + + if (symbol == model->mMaterialList[i]) // found the binding + { + instance_mat = inst_materials[j]; + } + } + } + + if (instance_mat) + { + domMaterial* material = daeSafeCast(instance_mat->getTarget().getElement()); + if (material) + { + domInstance_effect* instance_effect = + daeSafeCast(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); + if (instance_effect) + { + domEffect* effect = daeSafeCast(instance_effect->getUrl().getElement()); + if (effect) + { + domProfile_COMMON* profile = + daeSafeCast(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID()))); + if (profile) + { + import_material = profileToMaterial(profile); + } + } + } + } + } + + materials.push_back(import_material); + } + + return materials; +} + +LLImportMaterial LLModelLoader::profileToMaterial(domProfile_COMMON* material) +{ + LLImportMaterial mat; + mat.mFullbright = FALSE; + + daeElement* diffuse = material->getDescendant("diffuse"); + if (diffuse) + { + domCommon_color_or_texture_type_complexType::domTexture* texture = + daeSafeCast(diffuse->getDescendant("texture")); + if (texture) + { + domCommon_newparam_type_Array newparams = material->getNewparam_array(); + for (S32 i = 0; i < newparams.getCount(); i++) + { + domFx_surface_common* surface = newparams[i]->getSurface(); + if (surface) + { + domFx_surface_init_common* init = surface->getFx_surface_init_common(); + if (init) + { + domFx_surface_init_from_common_Array init_from = init->getInit_from_array(); + + if (init_from.getCount() > i) + { + domImage* image = daeSafeCast(init_from[i]->getValue().getElement()); + if (image) + { + // we only support init_from now - embedded data will come later + domImage::domInit_from* init = image->getInit_from(); + if (init) + { + std::string filename = cdom::uriToNativePath(init->getValue().str()); + + mat.mDiffuseMap = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + filename, TRUE, LLViewerTexture::BOOST_PREVIEW); + mat.mDiffuseMap->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, this->mPreview, NULL, FALSE); + + mat.mDiffuseMap->forceToSaveRawImage(); + mat.mDiffuseMapFilename = filename; + mat.mDiffuseMapLabel = getElementLabel(material); + } + } + } + } + } + } + } + + domCommon_color_or_texture_type_complexType::domColor* color = + daeSafeCast(diffuse->getDescendant("color")); + if (color) + { + domFx_color_common domfx_color = color->getValue(); + LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); + mat.mDiffuseColor = value; + } + } + + daeElement* emission = material->getDescendant("emission"); + if (emission) + { + LLColor4 emission_color = getDaeColor(emission); + if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25) + { + mat.mFullbright = TRUE; + } + } + + return mat; +} + +// try to get a decent label for this element +std::string LLModelLoader::getElementLabel(daeElement *element) +{ + // if we have a name attribute, use it + std::string name = element->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if we have an ID attribute, use it + if (element->getID()) + { + return std::string(element->getID()); + } + + // if we have a parent, use it + daeElement* parent = element->getParent(); + if (parent) + { + // if parent has a name, use it + std::string name = parent->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if parent has an ID, use it + if (parent->getID()) + { + return std::string(parent->getID()); + } + } + + // try to use our type + daeString element_name = element->getElementName(); + if (element_name) + { + return std::string(element_name); + } + + // if all else fails, use "object" + return std::string("object"); +} + +LLColor4 LLModelLoader::getDaeColor(daeElement* element) +{ + LLColor4 value; + domCommon_color_or_texture_type_complexType::domColor* color = + daeSafeCast(element->getDescendant("color")); + if (color) + { + domFx_color_common domfx_color = color->getValue(); + value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); + } + + return value; +} + +//----------------------------------------------------------------------------- +// LLModelPreview +//----------------------------------------------------------------------------- + +LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) +: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex(NULL) +{ + mNeedsUpdate = TRUE; + mCameraDistance = 0.f; + mCameraYaw = 0.f; + mCameraPitch = 0.f; + mCameraZoom = 1.f; + mTextureName = 0; + mPreviewLOD = 0; + mModelLoader = NULL; + mMaxTriangleLimit = 0; + mDirty = false; + mGenLOD = false; + mLoading = false; + mGroup = 0; + mBuildShareTolerance = 0.f; + mBuildQueueMode = GLOD_QUEUE_GREEDY; + mBuildBorderMode = GLOD_BORDER_UNLOCK; + mBuildOperator = GLOD_OPERATOR_HALF_EDGE_COLLAPSE; + + mViewOption["show_textures"] = false; + + mFMP = fmp; + + glodInit(); +} + +LLModelPreview::~LLModelPreview() +{ + if (mModelLoader) + { + delete mModelLoader; + mModelLoader = NULL; + } + + //*HACK : *TODO : turn this back on when we understand why this crashes + //glodShutdown(); +} + +U32 LLModelPreview::calcResourceCost() +{ + assert_main_thread(); + + rebuildUploadData(); + + if ( mModelLoader->getLoadState() != LLModelLoader::ERROR_PARSING ) + { + mFMP->childEnable("ok_btn"); + } + + U32 cost = 0; + std::set accounted; + U32 num_points = 0; + U32 num_hulls = 0; + + F32 debug_scale = mFMP->childGetValue("import_scale").asReal(); + + F32 streaming_cost = 0.f; + F32 physics_cost = 0.f; + for (U32 i = 0; i < mUploadData.size(); ++i) + { + LLModelInstance& instance = mUploadData[i]; + + if (accounted.find(instance.mModel) == accounted.end()) + { + accounted.insert(instance.mModel); + + LLModel::convex_hull_decomposition& decomp = + instance.mLOD[LLModel::LOD_PHYSICS] ? + instance.mLOD[LLModel::LOD_PHYSICS]->mConvexHullDecomp : + instance.mModel->mConvexHullDecomp; + + LLSD ret = LLModel::writeModel( + "", + instance.mLOD[4], + instance.mLOD[3], + instance.mLOD[2], + instance.mLOD[1], + instance.mLOD[0], + decomp, + mFMP->childGetValue("upload_skin").asBoolean(), + mFMP->childGetValue("upload_joints").asBoolean(), + TRUE); + cost += gMeshRepo.calcResourceCost(ret); + + num_hulls += decomp.size(); + for (U32 i = 0; i < decomp.size(); ++i) + { + num_points += decomp[i].size(); + } + + //calculate streaming cost + LLMatrix4 transformation = instance.mTransform; + + LLVector3 position = LLVector3(0, 0, 0) * transformation; + + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); + + F32 radius = scale.length()*debug_scale; + + streaming_cost += LLMeshRepository::getStreamingCost(ret, radius); + } + } + + //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",num_hulls)); + //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",num_points)); + F32 scale = mFMP->childGetValue("import_scale").asReal()*2.f; + + mDetailsSignal(mPreviewScale[0]*scale, mPreviewScale[1]*scale, mPreviewScale[2]*scale, streaming_cost, physics_cost); + + updateStatusMessages(); + + return cost; +} + +void LLFloaterModelPreview::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost) +{ + childSetTextArg("import_dimensions", "[X]", llformat("%.3f", x)); + childSetTextArg("import_dimensions", "[Y]", llformat("%.3f", y)); + childSetTextArg("import_dimensions", "[Z]", llformat("%.3f", z)); + childSetTextArg("streaming cost", "[COST]", llformat("%.3f", streaming_cost)); + childSetTextArg("physics cost", "[COST]", llformat("%.3f", physics_cost)); +} + +void LLModelPreview::rebuildUploadData() +{ + assert_main_thread(); + + mUploadData.clear(); + mTextureSet.clear(); + + //fill uploaddata instance vectors from scene data + + std::string requested_name = mFMP->getChild("description_form")->getValue().asString(); + + + LLSpinCtrl* scale_spinner = mFMP->getChild("import_scale"); + + if (!scale_spinner) + { + llerrs << "floater_model_preview.xml MUST contain import_scale spinner." << llendl; + } + + F32 scale = scale_spinner->getValue().asReal(); + + LLMatrix4 scale_mat; + scale_mat.initScale(LLVector3(scale, scale, scale)); + + F32 max_scale = 0.f; + + if ( mBaseScene.size() > 0 ) + { + mFMP->childEnable("ok_btn"); + } + + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) + { //for each transform in scene + LLMatrix4 mat = iter->first; + + // compute position + LLVector3 position = LLVector3(0, 0, 0) * mat; + + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * mat - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * mat - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * mat - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + + max_scale = llmax(llmax(llmax(max_scale, x_length), y_length), z_length); + + mat *= scale_mat; + + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { //for each instance with said transform applied + LLModelInstance instance = *model_iter; + + LLModel* base_model = instance.mModel; + if (base_model) + { + base_model->mRequestedLabel = requested_name; + } + + S32 idx = 0; + for (idx = 0; idx < mBaseModel.size(); ++idx) + { //find reference instance for this model + if (mBaseModel[idx] == base_model) + { + break; + } + } + + for (U32 i = 0; i < LLModel::NUM_LODS; i++) + { //fill LOD slots based on reference model index + if (!mModel[i].empty()) + { + instance.mLOD[i] = mModel[i][idx]; + } + else + { + instance.mLOD[i] = NULL; + } + } + + instance.mTransform = mat; + mUploadData.push_back(instance); + } + } + + F32 max_import_scale = DEFAULT_MAX_PRIM_SCALE/max_scale; + + scale_spinner->setMaxValue(max_import_scale); + + if (max_import_scale < scale) + { + scale_spinner->setValue(max_import_scale); + } + +} + + +void LLModelPreview::clearModel(S32 lod) +{ + if (lod < 0 || lod > LLModel::LOD_PHYSICS) + { + return; + } + + mVertexBuffer[lod].clear(); + mModel[lod].clear(); + mScene[lod].clear(); +} + +void LLModelPreview::loadModel(std::string filename, S32 lod) +{ + assert_main_thread(); + + LLMutexLock lock(this); + + if (mModelLoader) + { + delete mModelLoader; + mModelLoader = NULL; + } + + if (filename.empty()) + { + if (mBaseModel.empty()) + { + // this is the initial file picking. Close the whole floater + // if we don't have a base model to show for high LOD. + mFMP->closeFloater(false); + } + + mLoading = false; + return; + } + + mLODFile[lod] = filename; + + if (lod == LLModel::LOD_HIGH) + { + clearGLODGroup(); + } + + mModelLoader = new LLModelLoader(filename, lod, this); + + mModelLoader->start(); + + mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); + + setPreviewLOD(lod); + + if ( mModelLoader->getLoadState() == LLModelLoader::ERROR_PARSING ) + { + mFMP->childDisable("ok_btn"); + } + + if (lod == mPreviewLOD) + { + mFMP->childSetText("lod_file", mLODFile[mPreviewLOD]); + } + else if (lod == LLModel::LOD_PHYSICS) + { + mFMP->childSetText("physics_file", mLODFile[lod]); + } + + mFMP->openFloater(); +} + +void LLModelPreview::setPhysicsFromLOD(S32 lod) +{ + assert_main_thread(); + + if (lod >= 0 && lod <= 3) + { + mModel[LLModel::LOD_PHYSICS] = mModel[lod]; + mScene[LLModel::LOD_PHYSICS] = mScene[lod]; + mLODFile[LLModel::LOD_PHYSICS].clear(); + mFMP->childSetText("physics_file", mLODFile[LLModel::LOD_PHYSICS]); + mVertexBuffer[LLModel::LOD_PHYSICS].clear(); + rebuildUploadData(); + refresh(); + updateStatusMessages(); + } +} + +void LLModelPreview::clearIncompatible(S32 lod) +{ + for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) + { //clear out any entries that aren't compatible with this model + if (i != lod) + { + if (mModel[i].size() != mModel[lod].size()) + { + mModel[i].clear(); + mScene[i].clear(); + mVertexBuffer[i].clear(); + + if (i == LLModel::LOD_HIGH) + { + mBaseModel = mModel[lod]; + clearGLODGroup(); + mBaseScene = mScene[lod]; + mVertexBuffer[5].clear(); + } + } + } + } +} + +void LLModelPreview::clearGLODGroup() +{ + if (mGroup) + { + for (std::map, U32>::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) + { + glodDeleteObject(iter->second); + stop_gloderror(); + } + mObject.clear(); + + glodDeleteGroup(mGroup); + stop_gloderror(); + mGroup = 0; + } +} + +void LLModelPreview::loadModelCallback(S32 lod) +{ + assert_main_thread(); + + LLMutexLock lock(this); + if (!mModelLoader) + { + return; + } + + mModel[lod] = mModelLoader->mModelList; + mScene[lod] = mModelLoader->mScene; + mVertexBuffer[lod].clear(); + + if (lod == LLModel::LOD_PHYSICS) + { + mPhysicsMesh.clear(); + } + + setPreviewLOD(lod); + + + if (lod == LLModel::LOD_HIGH) + { //save a copy of the highest LOD for automatic LOD manipulation + if (mBaseModel.empty()) + { //first time we've loaded a model, auto-gen LoD + mGenLOD = true; + } + + mBaseModel = mModel[lod]; + clearGLODGroup(); + + mBaseScene = mScene[lod]; + mVertexBuffer[5].clear(); + } + + clearIncompatible(lod); + + mDirty = true; + + if (lod == LLModel::LOD_HIGH) + { + resetPreviewTarget(); + } + + mLoading = false; + refresh(); +} + +void LLModelPreview::resetPreviewTarget() +{ + mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; + mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; + setPreviewTarget(mPreviewScale.magVec()*2.f); +} + +void LLModelPreview::generateNormals() +{ + assert_main_thread(); + + S32 which_lod = mPreviewLOD; + + + if (which_lod > 4 || which_lod < 0 || + mModel[which_lod].empty()) + { + return; + } + + F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); + + angle_cutoff *= DEG_TO_RAD; + + if (which_lod == 3 && !mBaseModel.empty()) + { + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { + (*iter)->generateNormals(angle_cutoff); + } + + mVertexBuffer[5].clear(); + } + + for (LLModelLoader::model_list::iterator iter = mModel[which_lod].begin(); iter != mModel[which_lod].end(); ++iter) + { + (*iter)->generateNormals(angle_cutoff); + } + + mVertexBuffer[which_lod].clear(); + refresh(); + +} + +void LLModelPreview::consolidate() +{ + std::map > composite; + + LLMatrix4 identity; + + //bake out each node in current scene to composite + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { //for each transform in current scene + LLMatrix4 mat = iter->first; + glh::matrix4f inv_trans = glh::matrix4f((F32*) mat.mMatrix).inverse().transpose(); + LLMatrix4 norm_mat(inv_trans.m); + + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { //for each instance with that transform + LLModelInstance& source_instance = *model_iter; + LLModel* source = source_instance.mModel; + + if (!validate_model(source)) + { + llerrs << "Invalid model found!" << llendl; + } + + for (S32 i = 0; i < source->getNumVolumeFaces(); ++i) + { //for each face in instance + const LLVolumeFace& src_face = source->getVolumeFace(i); + LLImportMaterial& source_material = source_instance.mMaterial[i]; + + //get model in composite that is composite for this material + LLModel* model = NULL; + + if (composite.find(source_material) != composite.end()) + { + model = composite[source_material].rbegin()->mModel; + if (model->getVolumeFace(0).mNumVertices + src_face.mNumVertices > 65535) + { + model = NULL; + } + } + + if (model == NULL) + { //no model found, make new model + std::vector materials; + materials.push_back(source_material); + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + model = new LLModel(volume_params, 0.f); + model->mLabel = source->mLabel; + model->setNumVolumeFaces(0); + composite[source_material].push_back(LLModelInstance(model, identity, materials)); + } + + model->appendFace(src_face, source->mMaterialList[i], mat, norm_mat); + } + } + } + + + //condense composite into as few LLModel instances as possible + LLModelLoader::model_list new_model; + std::vector instance_list; + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + + std::vector empty_material; + LLModelInstance cur_instance(new LLModel(volume_params, 0.f), identity, empty_material); + cur_instance.mModel->setNumVolumeFaces(0); + + BOOL first_transform = TRUE; + + LLModelLoader::scene new_scene; + LLVector3 min,max; + + for (std::map >::iterator iter = composite.begin(); + iter != composite.end(); + ++iter) + { + std::map >::iterator next_iter = iter; ++next_iter; + + for (std::vector::iterator instance_iter = iter->second.begin(); + instance_iter != iter->second.end(); + ++instance_iter) + { + LLModel* source = instance_iter->mModel; + + if (instance_iter->mMaterial.size() != 1) + { + llerrs << "WTF?" << llendl; + } + + if (source->getNumVolumeFaces() != 1) + { + llerrs << "WTF?" << llendl; + } + + if (source->mMaterialList.size() != 1) + { + llerrs << "WTF?" << llendl; + } + + cur_instance.mModel->addFace(source->getVolumeFace(0)); + cur_instance.mMaterial.push_back(instance_iter->mMaterial[0]); + cur_instance.mModel->mMaterialList.push_back(source->mMaterialList[0]); + + BOOL last_model = FALSE; + + std::vector::iterator next_instance = instance_iter; ++next_instance; + + if (next_iter == composite.end() && + next_instance == iter->second.end()) + { + last_model = TRUE; + } + + if (last_model || cur_instance.mModel->getNumVolumeFaces() >= MAX_MODEL_FACES) + { + cur_instance.mModel->mLabel = source->mLabel; + + cur_instance.mModel->optimizeVolumeFaces(); + cur_instance.mModel->normalizeVolumeFaces(); + + if (!validate_model(cur_instance.mModel)) + { + llerrs << "Invalid model detected." << llendl; + } + + new_model.push_back(cur_instance.mModel); + + LLMatrix4 transformation = LLMatrix4(); + + // adjust the transformation to compensate for mesh normalization + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + cur_instance.mModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(mesh_translation_vector); + mesh_translation *= transformation; + transformation = mesh_translation; + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= transformation; + transformation = mesh_scale; + + cur_instance.mTransform = transformation; + + new_scene[transformation].push_back(cur_instance); + stretch_extents(cur_instance.mModel, transformation, min, max, first_transform); + + if (!last_model) + { + cur_instance = LLModelInstance(new LLModel(volume_params, 0.f), identity, empty_material); + cur_instance.mModel->setNumVolumeFaces(0); + } + } + } + } + + mScene[mPreviewLOD] = new_scene; + mModel[mPreviewLOD] = new_model; + mVertexBuffer[mPreviewLOD].clear(); + + if (mPreviewLOD == LLModel::LOD_HIGH) + { + mBaseScene = new_scene; + mBaseModel = new_model; + clearGLODGroup(); + mVertexBuffer[5].clear(); + } + + mPreviewTarget = (min+max)*0.5f; + mPreviewScale = (max-min)*0.5f; + setPreviewTarget(mPreviewScale.magVec()*2.f); + + clearIncompatible(mPreviewLOD); + + mResourceCost = calcResourceCost(); + refresh(); +} + +void LLModelPreview::clearMaterials() +{ + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { //for each transform in current scene + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { //for each instance with that transform + LLModelInstance& source_instance = *model_iter; + LLModel* source = source_instance.mModel; + + for (S32 i = 0; i < source->getNumVolumeFaces(); ++i) + { //for each face in instance + LLImportMaterial& source_material = source_instance.mMaterial[i]; + + //clear material info + source_material.mDiffuseColor = LLColor4(1,1,1,1); + source_material.mDiffuseMap = NULL; + source_material.mDiffuseMapFilename.clear(); + source_material.mDiffuseMapLabel.clear(); + source_material.mFullbright = false; + } + } + } + + mVertexBuffer[mPreviewLOD].clear(); + + if (mPreviewLOD == LLModel::LOD_HIGH) + { + mBaseScene = mScene[mPreviewLOD]; + mBaseModel = mModel[mPreviewLOD]; + clearGLODGroup(); + mVertexBuffer[5].clear(); + } + + mResourceCost = calcResourceCost(); + refresh(); +} + +bool LLModelPreview::containsRiggedAsset( void ) +{ + //loop through the models and determine if any of them contained a rigged asset, and if so + //return true. + //This is used to cleanup the joint positions after a preview. + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { + LLModel* pModel = *iter; + if ( pModel->mAlternateBindMatrix.size() > 0 ) + { + return true; + } + } + return false; +} +void LLModelPreview::genLODs(S32 which_lod, U32 decimation) +{ + if (mBaseModel.empty()) + { + return; + } + + if (which_lod == LLModel::LOD_PHYSICS) + { //clear physics mesh map + mPhysicsMesh.clear(); + } + + LLVertexBuffer::unbind(); + + stop_gloderror(); + static U32 cur_name = 1; + + S32 limit = -1; + + U32 triangle_count = 0; + + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { + LLModel* mdl = *iter; + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + triangle_count += mdl->getVolumeFace(i).mNumIndices/3; + } + } + + U32 base_triangle_count = triangle_count; + + U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + U32 lod_mode = 0; + + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode"); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } + + F32 lod_error_threshold = mFMP->childGetValue("lod_error_threshold").asReal(); + + if (lod_mode == 0) + { + lod_mode = GLOD_TRIANGLE_BUDGET; + if (which_lod != -1) + { + limit = mFMP->childGetValue("lod_triangle_limit").asInteger(); + } + } + else + { + lod_mode = GLOD_ERROR_THRESHOLD; + } + + U32 build_operator = 0; + + iface = mFMP->childGetSelectionInterface("build_operator"); + if (iface) + { + build_operator = iface->getFirstSelectedIndex(); + } + + if (build_operator == 0) + { + build_operator = GLOD_OPERATOR_HALF_EDGE_COLLAPSE; + } + else + { + build_operator = GLOD_OPERATOR_EDGE_COLLAPSE; + } + + U32 queue_mode=0; + iface = mFMP->childGetSelectionInterface("queue_mode"); + if (iface) + { + queue_mode = iface->getFirstSelectedIndex(); + } + + if (queue_mode == 0) + { + queue_mode = GLOD_QUEUE_GREEDY; + } + else if (queue_mode == 1) + { + queue_mode = GLOD_QUEUE_LAZY; + } + else + { + queue_mode = GLOD_QUEUE_INDEPENDENT; + } + + U32 border_mode = 0; + + iface = mFMP->childGetSelectionInterface("border_mode"); + if (iface) + { + border_mode = iface->getFirstSelectedIndex(); + } + + if (border_mode == 0) + { + border_mode = GLOD_BORDER_UNLOCK; + } + else + { + border_mode = GLOD_BORDER_LOCK; + } + + bool object_dirty = false; + if (border_mode != mBuildBorderMode) + { + mBuildBorderMode = border_mode; + object_dirty = true; + } + + if (queue_mode != mBuildQueueMode) + { + mBuildQueueMode = queue_mode; + object_dirty = true; + } + + if (build_operator != mBuildOperator) + { + mBuildOperator = build_operator; + object_dirty = true; + } + + F32 share_tolerance = mFMP->childGetValue("share_tolerance").asReal(); + if (share_tolerance != mBuildShareTolerance) + { + mBuildShareTolerance = share_tolerance; + object_dirty = true; + } + + if (mGroup == 0) + { + object_dirty = true; + mGroup = cur_name++; + glodNewGroup(mGroup); + } + + if (object_dirty) + { + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { //build GLOD objects for each model in base model list + LLModel* mdl = *iter; + + if (mObject[mdl] != 0) + { + glodDeleteObject(mObject[mdl]); + } + + mObject[mdl] = cur_name++; + + glodNewObject(mObject[mdl], mGroup, GLOD_DISCRETE); + stop_gloderror(); + + if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) + { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation + mVertexBuffer[5].clear(); + } + + if (mVertexBuffer[5].empty()) + { + genBuffers(5, false); + } + + U32 tri_count = 0; + for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) + { + mVertexBuffer[5][mdl][i]->setBuffer(type_mask); + U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); + if (num_indices > 2) + { + glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); + } + tri_count += num_indices/3; + stop_gloderror(); + } + + glodObjectParameteri(mObject[mdl], GLOD_BUILD_OPERATOR, build_operator); + stop_gloderror(); + + glodObjectParameteri(mObject[mdl], GLOD_BUILD_QUEUE_MODE, queue_mode); + stop_gloderror(); + + glodObjectParameteri(mObject[mdl], GLOD_BUILD_BORDER_MODE, border_mode); + stop_gloderror(); + + glodObjectParameterf(mObject[mdl], GLOD_BUILD_SHARE_TOLERANCE, share_tolerance); + stop_gloderror(); + + glodBuildObject(mObject[mdl]); + stop_gloderror(); + } + } + + + S32 start = LLModel::LOD_HIGH; + S32 end = 0; + + if (which_lod != -1) + { + start = end = which_lod; + } + else + { + //SH-632 -- incremenet triangle count to avoid removing any triangles from + //highest LoD when auto-generating LoD + triangle_count++; + } + + + mMaxTriangleLimit = base_triangle_count; + + for (S32 lod = start; lod >= end; --lod) + { + if (which_lod == -1) + { + if (lod < start) + { + triangle_count /= decimation; + } + } + else + { + triangle_count = limit; + } + + mModel[lod].clear(); + mModel[lod].resize(mBaseModel.size()); + mVertexBuffer[lod].clear(); + + U32 actual_tris = 0; + U32 actual_verts = 0; + U32 submeshes = 0; + + glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); + stop_gloderror(); + + glodGroupParameteri(mGroup, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); + stop_gloderror(); + + glodGroupParameteri(mGroup, GLOD_MAX_TRIANGLES, triangle_count); + stop_gloderror(); + + glodGroupParameterf(mGroup, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, lod_error_threshold); + stop_gloderror(); + + glodAdaptGroup(mGroup); + stop_gloderror(); + + for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) + { + LLModel* base = mBaseModel[mdl_idx]; + + GLint patch_count = 0; + glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); + stop_gloderror(); + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); + + GLint* sizes = new GLint[patch_count*2]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); + stop_gloderror(); + + GLint* names = new GLint[patch_count]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); + stop_gloderror(); + + mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); + + LLModel* target_model = mModel[lod][mdl_idx]; + + for (GLint i = 0; i < patch_count; ++i) + { + LLPointer buff = new LLVertexBuffer(type_mask, 0); + + if (sizes[i*2+1] > 0 && sizes[i*2] > 0) + { + buff->allocateBuffer(sizes[i*2+1], sizes[i*2], true); + buff->setBuffer(type_mask); + glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, buff->getIndicesPointer()); + stop_gloderror(); + } + else + { //this face was eliminated, create a dummy triangle (one vertex, 3 indices, all 0) + buff->allocateBuffer(1, 3, true); + memset(buff->getMappedData(), 0, buff->getSize()); + memset(buff->getIndicesPointer(), 0, buff->getIndicesSize()); + } + + buff->validateRange(0, buff->getNumVerts()-1, buff->getNumIndices(), 0); + + LLStrider pos; + LLStrider norm; + LLStrider tc; + LLStrider index; + + buff->getVertexStrider(pos); + buff->getNormalStrider(norm); + buff->getTexCoord0Strider(tc); + buff->getIndexStrider(index); + + target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + actual_tris += buff->getNumIndices()/3; + actual_verts += buff->getNumVerts(); + ++submeshes; + + if (!validate_face(target_model->getVolumeFace(names[i]))) + { + llerrs << "Invalid face generated during LOD generation." << llendl; + } + } + + //blind copy skin weights and just take closest skin weight to point on + //decimated mesh for now (auto-generating LODs with skin weights is still a bit + //of an open problem). + target_model->mPosition = base->mPosition; + target_model->mSkinWeights = base->mSkinWeights; + target_model->mJointMap = base->mJointMap; + target_model->mJointList = base->mJointList; + target_model->mInvBindMatrix = base->mInvBindMatrix; + target_model->mBindShapeMatrix = base->mBindShapeMatrix; + target_model->mAlternateBindMatrix = base->mAlternateBindMatrix; + //copy material list + target_model->mMaterialList = base->mMaterialList; + + if (!validate_model(target_model)) + { + llerrs << "Invalid model generated when creating LODs" << llendl; + } + + delete [] sizes; + delete [] names; + } + + //rebuild scene based on mBaseScene + mScene[lod].clear(); + mScene[lod] = mBaseScene; + + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + LLModel* mdl = mBaseModel[i]; + LLModel* target = mModel[lod][i]; + if (target) + { + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + for (U32 j = 0; j < iter->second.size(); ++j) + { + if (iter->second[j].mModel == mdl) + { + iter->second[j].mModel = target; + } + } + } + } + } + } + + mResourceCost = calcResourceCost(); + + /*if (which_lod == -1 && mScene[LLModel::LOD_PHYSICS].empty()) + { //build physics scene + mScene[LLModel::LOD_PHYSICS] = mScene[LLModel::LOD_LOW]; + mModel[LLModel::LOD_PHYSICS] = mModel[LLModel::LOD_LOW]; + + for (U32 i = 1; i < mModel[LLModel::LOD_PHYSICS].size(); ++i) + { + mPhysicsQ.push(mModel[LLModel::LOD_PHYSICS][i]); + } + }*/ +} + +void LLModelPreview::updateStatusMessages() +{ + assert_main_thread(); + + //triangle/vertex/submesh count for each mesh asset for each lod + std::vector tris[LLModel::NUM_LODS]; + std::vector verts[LLModel::NUM_LODS]; + std::vector submeshes[LLModel::NUM_LODS]; + + //total triangle/vertex/submesh count for each lod + S32 total_tris[LLModel::NUM_LODS]; + S32 total_verts[LLModel::NUM_LODS]; + S32 total_submeshes[LLModel::NUM_LODS]; + + for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) + { + //initialize total for this lod to 0 + total_tris[lod] = total_verts[lod] = total_submeshes[lod] = 0; + + for (U32 i = 0; i < mModel[lod].size(); ++i) + { //for each model in the lod + S32 cur_tris = 0; + S32 cur_verts = 0; + S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); + + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); + cur_tris += face.mNumIndices/3; + cur_verts += face.mNumVertices; + } + + //add this model to the lod total + total_tris[lod] += cur_tris; + total_verts[lod] += cur_verts; + total_submeshes[lod] += cur_submeshes; + + //store this model's counts to asset data + tris[lod].push_back(cur_tris); + verts[lod].push_back(cur_verts); + submeshes[lod].push_back(cur_submeshes); + } + } + + if (mMaxTriangleLimit == 0) + { + mMaxTriangleLimit = total_tris[LLModel::LOD_HIGH]; + } + + + mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH])); + + std::string mesh_status_na = mFMP->getString("mesh_status_na"); + + S32 upload_status[LLModel::LOD_HIGH+1]; + + bool upload_ok = true; + + for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod) + { + upload_status[lod] = 0; + + std::string message = "mesh_status_good"; + + if (total_tris[lod] > 0) + { + mFMP->childSetText(lod_triangles_name[lod], llformat("%d", total_tris[lod])); + mFMP->childSetText(lod_vertices_name[lod], llformat("%d", total_verts[lod])); + } + else + { + if (lod == LLModel::LOD_HIGH) + { + upload_status[lod] = 2; + message = "mesh_status_missing_lod"; + } + else + { + for (S32 i = lod-1; i >= 0; --i) + { + if (total_tris[i] > 0) + { + upload_status[lod] = 2; + message = "mesh_status_missing_lod"; + } + } + } + + mFMP->childSetText(lod_triangles_name[lod], mesh_status_na); + mFMP->childSetText(lod_vertices_name[lod], mesh_status_na); + } + + const U32 lod_high = LLModel::LOD_HIGH; + + if (lod != lod_high) + { + if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) + { //number of submeshes is different + message = "mesh_status_submesh_mismatch"; + upload_status[lod] = 2; + } + else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) + { //number of meshes is different + message = "mesh_status_mesh_mismatch"; + upload_status[lod] = 2; + } + else if (!verts[lod].empty()) + { + for (U32 i = 0; i < verts[lod].size(); ++i) + { + S32 max_verts = i < verts[lod+1].size() ? verts[lod+1][i] : 0; + + if (max_verts > 0) + { + if (verts[lod][i] > max_verts) + { //too many vertices in this lod + message = "mesh_status_too_many_vertices"; + upload_status[lod] = 2; + } + } + } + } + } + + LLIconCtrl* icon = mFMP->getChild(lod_icon_name[lod]); + LLUIImagePtr img = LLUI::getUIImage(lod_status_image[upload_status[lod]]); + icon->setVisible(true); + icon->setImage(img); + + if (upload_status[lod] >= 2) + { + upload_ok = false; + } + + if (lod == mPreviewLOD) + { + mFMP->childSetText("lod_status_message_text", mFMP->getString(message)); + icon = mFMP->getChild("lod_status_message_icon"); + icon->setImage(img); + } + } + + bool errorStateFromLoader = mModelLoader->getLoadState() == LLModelLoader::ERROR_PARSING ? true : false; + + if ( upload_ok && !errorStateFromLoader ) + { + mFMP->childEnable("ok_btn"); + } + else + { + mFMP->childDisable("ok_btn"); + } + + //add up physics triangles etc + S32 start = 0; + S32 end = mModel[LLModel::LOD_PHYSICS].size(); + + S32 phys_tris = 0; + S32 phys_hulls = 0; + S32 phys_points = 0; + + for (S32 i = start; i < end; ++i) + { //add up hulls and points and triangles for selected mesh(es) + LLModel* model = mModel[LLModel::LOD_PHYSICS][i]; + S32 cur_submeshes = model->getNumVolumeFaces(); + + LLModel::convex_hull_decomposition& decomp = model->mConvexHullDecomp; + + if (!decomp.empty()) + { + phys_hulls += decomp.size(); + for (U32 i = 0; i < decomp.size(); ++i) + { + phys_points += decomp[i].size(); + } + } + else + { //choose physics shape OR decomposition, can't use both + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = model->getVolumeFace(j); + phys_tris += face.mNumIndices/3; + } + } + } + + if (phys_tris > 0) + { + mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", llformat("%d", phys_tris)); + } + else + { + mFMP->childSetTextArg("physics_triangles", "[TRIANGLES]", mesh_status_na); + } + + if (phys_hulls > 0) + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", llformat("%d", phys_hulls)); + mFMP->childSetTextArg("physics_points", "[POINTS]", llformat("%d", phys_points)); + } + else + { + mFMP->childSetTextArg("physics_hulls", "[HULLS]", mesh_status_na); + mFMP->childSetTextArg("physics_points", "[POINTS]", mesh_status_na); + } + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + if (phys_tris > 0 || phys_hulls > 0) + { + if (!fmp->isViewOptionEnabled("show_physics")) + { + fmp->enableViewOption("show_physics"); + mViewOption["show_physics"] = true; + } + } + else + { + fmp->disableViewOption("show_physics"); + mViewOption["show_physics"] = false; + + } + + //bool use_hull = fmp->childGetValue("physics_use_hull").asBoolean(); + + //fmp->childSetEnabled("physics_optimize", !use_hull); + + bool enable = phys_tris > 0 || phys_hulls > 0; + //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); + + //enable/disable "analysis" UI + LLPanel* panel = fmp->getChild("physics analysis"); + LLView* child = panel->getFirstChild(); + while (child) + { + child->setEnabled(enable); + child = panel->findNextSibling(child); + } + + enable = phys_hulls > 0; + //enable/disable "simplification" UI + panel = fmp->getChild("physics simplification"); + child = panel->getFirstChild(); + while (child) + { + child->setEnabled(enable); + child = panel->findNextSibling(child); + } + } + + const char* lod_controls[] = + { + "lod_mode", + "lod_triangle_limit", + "lod_error_tolerance", + "build_operator_text", + "queue_mode_text", + "border_mode_text", + "share_tolerance_text", + "build_operator", + "queue_mode", + "border_mode", + "share_tolerance" + }; + const U32 num_lod_controls = sizeof(lod_controls)/sizeof(char*); + + const char* file_controls[] = + { + "lod_browse", + "lod_file" + }; + const U32 num_file_controls = sizeof(file_controls)/sizeof(char*); + + if (fmp) + { + //enable/disable controls based on radio groups + if (mFMP->childGetValue("lod_from_file").asBoolean()) + { + fmp->mLODMode[mPreviewLOD] = 0; + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childEnable(file_controls[i]); + } + + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childDisable(lod_controls[i]); + } + } + else if (mFMP->childGetValue("lod_none").asBoolean()) + { + fmp->mLODMode[mPreviewLOD] = 2; + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childDisable(file_controls[i]); + } + + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childDisable(lod_controls[i]); + } + + if (!mModel[mPreviewLOD].empty()) + { + mModel[mPreviewLOD].clear(); + mScene[mPreviewLOD].clear(); + mVertexBuffer[mPreviewLOD].clear(); + + //this can cause phasing issues with the UI, so reenter this function and return + updateStatusMessages(); + return; + } + } + else + { // auto generate, also the default case for wizard which has no radio selection + fmp->mLODMode[mPreviewLOD] = 1; + + for (U32 i = 0; i < num_file_controls; ++i) + { + mFMP->childDisable(file_controls[i]); + } + + for (U32 i = 0; i < num_lod_controls; ++i) + { + mFMP->childEnable(lod_controls[i]); + } + + //if (threshold) + { + U32 lod_mode = 0; + LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode"); + if (iface) + { + lod_mode = iface->getFirstSelectedIndex(); + } + + LLSpinCtrl* threshold = mFMP->getChild("lod_error_threshold"); + LLSpinCtrl* limit = mFMP->getChild("lod_triangle_limit"); + + limit->setMaxValue(mMaxTriangleLimit); + limit->setValue(total_tris[mPreviewLOD]); + + if (lod_mode == 0) + { + limit->setVisible(true); + threshold->setVisible(false); + + limit->setMaxValue(mMaxTriangleLimit); + } + else + { + limit->setVisible(false); + threshold->setVisible(true); + } + } + } + } + + if (mFMP->childGetValue("physics_load_from_file").asBoolean()) + { + mFMP->childDisable("physics_lod_combo"); + mFMP->childEnable("physics_file"); + mFMP->childEnable("physics_browse"); + } + else + { + mFMP->childEnable("physics_lod_combo"); + mFMP->childDisable("physics_file"); + mFMP->childDisable("physics_browse"); + } +} + +void LLModelPreview::setPreviewTarget(F32 distance) +{ + mCameraDistance = distance; + mCameraZoom = 1.f; + mCameraPitch = 0.f; + mCameraYaw = 0.f; + mCameraOffset.clearVec(); +} + +void LLModelPreview::clearBuffers() +{ + for (U32 i = 0; i < 6; i++) + { + mVertexBuffer[i].clear(); + } +} + +void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) +{ + U32 tri_count = 0; + U32 vertex_count = 0; + U32 mesh_count = 0; + + LLModelLoader::model_list* model = NULL; + + if (lod < 0 || lod > 4) + { + model = &mBaseModel; + lod = 5; + } + else + { + model = &(mModel[lod]); + } + + if (!mVertexBuffer[lod].empty()) + { + mVertexBuffer[lod].clear(); + } + + mVertexBuffer[lod].clear(); + + LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); + + for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) + { + LLModel* mdl = *iter; + if (!mdl) + { + continue; + } + + LLModel* base_mdl = *base_iter; + base_iter++; + + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + const LLVolumeFace &vf = mdl->getVolumeFace(i); + U32 num_vertices = vf.mNumVertices; + U32 num_indices = vf.mNumIndices; + + if (!num_vertices || ! num_indices) + { + continue; + } + + LLVertexBuffer* vb = NULL; + + bool skinned = include_skin_weights && !mdl->mSkinWeights.empty(); + + U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + if (skinned) + { + mask |= LLVertexBuffer::MAP_WEIGHT4; + } + + vb = new LLVertexBuffer(mask, 0); + + vb->allocateBuffer(num_vertices, num_indices, TRUE); + + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + LLStrider index_strider; + LLStrider weights_strider; + + vb->getVertexStrider(vertex_strider); + vb->getNormalStrider(normal_strider); + vb->getTexCoord0Strider(tc_strider); + vb->getIndexStrider(index_strider); + + if (skinned) + { + vb->getWeight4Strider(weights_strider); + } + + LLVector4a::memcpyNonAliased16((F32*) vertex_strider.get(), (F32*) vf.mPositions, num_vertices*4*sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32)); + + if (skinned) + { + for (U32 i = 0; i < num_vertices; i++) + { + //find closest weight to vf.mVertices[i].mPosition + LLVector3 pos(vf.mPositions[i].getF32ptr()); + + const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); + + LLVector4 w(0,0,0,0); + if (weight_list.size() > 4) + { + llerrs << "WTF?" << llendl; + } + + for (U32 i = 0; i < weight_list.size(); ++i) + { + F32 wght = llmin(weight_list[i].mWeight, 0.999999f); + F32 joint = (F32) weight_list[i].mJointIdx; + w.mV[i] = joint + wght; + } + + *(weights_strider++) = w; + } + } + + // build indices + for (U32 i = 0; i < num_indices; i++) + { + *(index_strider++) = vf.mIndices[i]; + } + + mVertexBuffer[lod][mdl].push_back(vb); + + vertex_count += num_vertices; + tri_count += num_indices/3; + ++mesh_count; + + } + } +} + +void LLModelPreview::update() +{ + if (mDirty) + { + mDirty = false; + mResourceCost = calcResourceCost(); + refresh(); + updateStatusMessages(); + } + + if (mGenLOD) + { + mGenLOD = false; + genLODs(); + refresh(); + updateStatusMessages(); + } + +} + +//----------------------------------------------------------------------------- +// render() +//----------------------------------------------------------------------------- +BOOL LLModelPreview::render() +{ + assert_main_thread(); + + LLMutexLock lock(this); + mNeedsUpdate = FALSE; + + bool edges = mViewOption["show_edges"]; + bool joint_positions = mViewOption["show_joint_positions"]; + bool skin_weight = mViewOption["show_skin_weight"]; + bool textures = mViewOption["show_textures"]; + bool physics = mViewOption["show_physics"]; + + S32 width = getWidth(); + S32 height = getHeight(); + + LLGLSUIDefault def; + LLGLDisable no_blend(GL_BLEND); + LLGLEnable cull(GL_CULL_FACE); + LLGLDepthTest depth(GL_TRUE); + LLGLDisable fog(GL_FOG); + + { + //clear background to blue + glMatrixMode(GL_PROJECTION); + gGL.pushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, width, 0.0f, height, -1.0f, 1.0f); + + glMatrixMode(GL_MODELVIEW); + gGL.pushMatrix(); + glLoadIdentity(); + + gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); + + gl_rect_2d_simple( width, height ); + + glMatrixMode(GL_PROJECTION); + gGL.popMatrix(); + + glMatrixMode(GL_MODELVIEW); + gGL.popMatrix(); + } + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + + bool has_skin_weights = false; + bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); + bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + if (!model->mSkinWeights.empty()) + { + has_skin_weights = true; + } + } + } + + if (has_skin_weights) + { //model has skin weights, enable view options for skin weights and joint positions + if (fmp) + { + fmp->enableViewOption("show_skin_weight"); + fmp->setViewOptionEnabled("show_joint_positions", skin_weight); + } + mFMP->childEnable("upload_skin"); + } + else + { + mFMP->childDisable("upload_skin"); + if (fmp) + { + mViewOption["show_skin_weight"] = false; + fmp->disableViewOption("show_skin_weight"); + fmp->disableViewOption("show_joint_positions"); + } + skin_weight = false; + } + + if (upload_skin && !has_skin_weights) + { //can't upload skin weights if model has no skin weights + mFMP->childSetValue("upload_skin", false); + upload_skin = false; + } + + if (!upload_skin && upload_joints) + { //can't upload joints if not uploading skin weights + mFMP->childSetValue("upload_joints", false); + upload_joints = false; + } + + mFMP->childSetEnabled("upload_joints", upload_skin); + + F32 explode = mFMP->childGetValue("physics_explode").asReal(); + + glClear(GL_DEPTH_BUFFER_BIT); + + LLRect preview_rect = mFMP->getChildView("preview_panel")->getRect(); + F32 aspect = (F32) preview_rect.getWidth()/preview_rect.getHeight(); + + LLViewerCamera::getInstance()->setAspect(aspect); + + LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); + + LLVector3 offset = mCameraOffset; + LLVector3 target_pos = mPreviewTarget+offset; + + F32 z_near = 0.001f; + F32 z_far = mCameraDistance+mPreviewScale.magVec()+mCameraOffset.magVec(); + + if (skin_weight) + { + target_pos = gAgentAvatarp->getPositionAgent(); + z_near = 0.01f; + z_far = 1024.f; + mCameraDistance = 16.f; + + //render avatar previews every frame + refresh(); + } + + glLoadIdentity(); + gPipeline.enableLightsPreview(); + + LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * + LLQuaternion(mCameraYaw, LLVector3::z_axis); + + LLQuaternion av_rot = camera_rot; + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + + LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); + + stop_glerror(); + + gGL.pushMatrix(); + const F32 BRIGHTNESS = 0.9f; + gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); + + LLGLEnable normalize(GL_NORMALIZE); + + if (!mBaseModel.empty() && mVertexBuffer[5].empty()) + { + genBuffers(-1, skin_weight); + //genBuffers(3); + //genLODs(); + } + + if (!mModel[mPreviewLOD].empty()) + { + bool regen = mVertexBuffer[mPreviewLOD].empty(); + if (!regen) + { + const std::vector >& vb_vec = mVertexBuffer[mPreviewLOD].begin()->second; + if (!vb_vec.empty()) + { + const LLVertexBuffer* buff = vb_vec[0]; + regen = buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) != skin_weight; + } + } + + if (regen) + { + genBuffers(mPreviewLOD, skin_weight); + } + + if (!skin_weight) + { + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[mPreviewLOD]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + glMultMatrixf((GLfloat*) mat.mMatrix); + + for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + + if (textures) + { + glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); + if (i < instance.mMaterial.size() && instance.mMaterial[i].mDiffuseMap.notNull()) + { + gGL.getTexUnit(0)->bind(instance.mMaterial[i].mDiffuseMap, true); + if (instance.mMaterial[i].mDiffuseMap->getDiscardLevel() > -1) + { + mTextureSet.insert(instance.mMaterial[i].mDiffuseMap); + } + } + } + else + { + glColor4f(1,1,1,1); + } + + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + glColor3f(0.4f, 0.4f, 0.4f); + + if (edges) + { + glLineWidth(3.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + gGL.popMatrix(); + } + + if (physics) + { + glClear(GL_DEPTH_BUFFER_BIT); + LLGLEnable blend(GL_BLEND); + gGL.blendFunc(LLRender::BF_ONE, LLRender::BF_ZERO); + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + + LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; + + if (!model) + { + continue; + } + + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; + + glMultMatrixf((GLfloat*) mat.mMatrix); + + + bool render_mesh = true; + + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); + + std::map, std::vector > >::iterator iter = + mPhysicsMesh.find(model); + if (iter != mPhysicsMesh.end()) + { //render hull instead of mesh + render_mesh = false; + for (U32 i = 0; i < iter->second.size(); ++i) + { + if (explode > 0.f) + { + gGL.pushMatrix(); + + LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters; + offset *= explode; + + gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); + } + + static std::vector hull_colors; + + if (i+1 >= hull_colors.size()) + { + hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 255)); + } + + LLVertexBuffer* buff = iter->second[i]; + if (buff) + { + buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL); + + glColor4ubv(hull_colors[i].mV); + buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts()); + } + + if (explode > 0.f) + { + gGL.popMatrix(); + } + } + } + } + + if (render_mesh) + { + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; + + buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + glColor4f(0.4f, 0.4f, 0.0f, 0.4f); + + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + + glColor3f(1.f, 1.f, 0.f); + + glLineWidth(3.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + + gGL.popMatrix(); + } + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + } + else + { + LLVOAvatarSelf* avatar = gAgentAvatarp; + target_pos = avatar->getPositionAgent(); + + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + if (joint_positions) + { + avatar->renderCollisionVolumes(); + } + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + + if (!model->mSkinWeights.empty()) + { + for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + const LLVolumeFace& face = model->getVolumeFace(i); + + LLStrider position; + buffer->getVertexStrider(position); + + LLStrider weight; + buffer->getWeight4Strider(weight); + + //quick 'n dirty software vertex skinning + + //build matrix palette + LLMatrix4 mat[64]; + for (U32 j = 0; j < model->mJointList.size(); ++j) + { + LLJoint* joint = avatar->getJoint(model->mJointList[j]); + if (joint) + { + mat[j] = model->mInvBindMatrix[j]; + mat[j] *= joint->getWorldMatrix(); + } + } + + for (U32 j = 0; j < buffer->getRequestedVerts(); ++j) + { + LLMatrix4 final_mat; + final_mat.mMatrix[0][0] = final_mat.mMatrix[1][1] = final_mat.mMatrix[2][2] = final_mat.mMatrix[3][3] = 0.f; + + LLVector4 wght; + S32 idx[4]; + + F32 scale = 0.f; + for (U32 k = 0; k < 4; k++) + { + F32 w = weight[j].mV[k]; + + idx[k] = (S32) floorf(w); + wght.mV[k] = w - floorf(w); + scale += wght.mV[k]; + } + + wght *= 1.f/scale; + + for (U32 k = 0; k < 4; k++) + { + F32* src = (F32*) mat[idx[k]].mMatrix; + F32* dst = (F32*) final_mat.mMatrix; + + F32 w = wght.mV[k]; + + for (U32 l = 0; l < 16; l++) + { + dst[l] += src[l]*w; + } + } + + //VECTORIZE THIS + LLVector3 v(face.mPositions[j].getF32ptr()); + + v = v * model->mBindShapeMatrix; + v = v * final_mat; + + position[j] = v; + } + + buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + glColor3f(0.4f, 0.4f, 0.4f); + + if (edges) + { + glLineWidth(3.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + } + } + } + } + } + + gGL.popMatrix(); + + return TRUE; +} + +//----------------------------------------------------------------------------- +// refresh() +//----------------------------------------------------------------------------- +void LLModelPreview::refresh() +{ + mNeedsUpdate = TRUE; +} + +//----------------------------------------------------------------------------- +// rotate() +//----------------------------------------------------------------------------- +void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) +{ + mCameraYaw = mCameraYaw + yaw_radians; + + mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); +} + +//----------------------------------------------------------------------------- +// zoom() +//----------------------------------------------------------------------------- +void LLModelPreview::zoom(F32 zoom_amt) +{ + F32 new_zoom = mCameraZoom+zoom_amt; + + mCameraZoom = llclamp(new_zoom, 1.f, 10.f); +} + +void LLModelPreview::pan(F32 right, F32 up) +{ + mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); + mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); +} + +void LLModelPreview::setPreviewLOD(S32 lod) +{ + lod = llclamp(lod, 0, (S32) LLModel::LOD_HIGH); + + if (lod != mPreviewLOD) + { + mPreviewLOD = lod; + + LLComboBox* combo_box = mFMP->getChild("preview_lod_combo"); + combo_box->setCurrentByIndex((NUM_LOD-1)-mPreviewLOD); // combo box list of lods is in reverse order + mFMP->childSetTextArg("lod_table_footer", "[DETAIL]", mFMP->getString(lod_name[mPreviewLOD])); + mFMP->childSetText("lod_file", mLODFile[mPreviewLOD]); + + LLColor4 highlight_color = LLUIColorTable::instance().getColor("MeshImportTableHighlightColor"); + LLColor4 normal_color = LLUIColorTable::instance().getColor("MeshImportTableNormalColor"); + + for (S32 i = 0; i <= LLModel::LOD_HIGH; ++i) + { + const LLColor4& color = (i == lod) ? highlight_color : normal_color; + + mFMP->childSetColor(lod_status_name[i], color); + mFMP->childSetColor(lod_label_name[i], color); + mFMP->childSetColor(lod_triangles_name[i], color); + mFMP->childSetColor(lod_vertices_name[i], color); + } + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + LLRadioGroup* radio = fmp->getChild("lod_file_or_limit"); + radio->selectNthItem(fmp->mLODMode[mPreviewLOD]); + } + } + refresh(); + updateStatusMessages(); +} + +//static +void LLFloaterModelPreview::onBrowseLOD(void* data) +{ + assert_main_thread(); + + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; + mp->loadModel(mp->mModelPreview->mPreviewLOD); +} + +//static +void LLFloaterModelPreview::onUpload(void* user_data) +{ + assert_main_thread(); + + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; + + mp->mModelPreview->rebuildUploadData(); + + gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale, + mp->childGetValue("upload_textures").asBoolean(), mp->childGetValue("upload_skin"), mp->childGetValue("upload_joints")); + + mp->closeFloater(false); +} + + +//static +void LLFloaterModelPreview::onClearMaterials(void* user_data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; + mp->mModelPreview->clearMaterials(); +} + +//static +void LLFloaterModelPreview::refresh(LLUICtrl* ctrl, void* user_data) +{ + sInstance->mModelPreview->mDirty = true; +} + +void LLFloaterModelPreview::updateResourceCost() +{ + U32 cost = mModelPreview->mResourceCost; + childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",cost)); +} + +//static +void LLModelPreview::textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ) +{ + LLModelPreview* preview = (LLModelPreview*) userdata; + preview->refresh(); +} + +LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LLModel* mdl) +{ + mStage = stage; + mContinue = 1; + mModel = mdl; + mDecompID = &mdl->mDecompID; + mParams = sInstance->mDecompParams; + + //copy out positions and indices + if (mdl) + { + U16 index_offset = 0; + + mPositions.clear(); + mIndices.clear(); + + //queue up vertex positions and indices + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = mdl->getVolumeFace(i); + if (mPositions.size() + face.mNumVertices > 65535) + { + continue; + } + + for (U32 j = 0; j < face.mNumVertices; ++j) + { + mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr())); + } + + for (U32 j = 0; j < face.mNumIndices; ++j) + { + mIndices.push_back(face.mIndices[j]+index_offset); + } + + index_offset += face.mNumVertices; + } + } +} + +void LLFloaterModelPreview::setStatusMessage(const std::string& msg) +{ + LLMutexLock lock(mStatusLock); + mStatusMessage = msg; +} + +S32 LLFloaterModelPreview::DecompRequest::statusCallback(const char* status, S32 p1, S32 p2) +{ + setStatusMessage(llformat("%s: %d/%d", status, p1, p2)); + if (LLFloaterModelPreview::sInstance) + { + LLFloaterModelPreview::sInstance->setStatusMessage(mStatusMessage); + } + + return mContinue; +} + +void LLFloaterModelPreview::DecompRequest::completed() +{ //called from the main thread + mModel->setConvexHullDecomposition(mHull); + + if (sInstance) + { + if (sInstance->mModelPreview) + { + sInstance->mModelPreview->mPhysicsMesh[mModel] = mHullMesh; + sInstance->mModelPreview->mDirty = true; + LLFloaterModelPreview::sInstance->mModelPreview->refresh(); + } + + sInstance->mCurRequest.erase(this); + } +} diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 11c2447da5..a791f3c44c 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -146,6 +146,8 @@ public: static void onMouseCaptureLostModelPreview(LLMouseHandler*); static void setUploadAmount(S32 amount) { sUploadAmount = amount; } + + void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost); static void onBrowseLOD(void* data); @@ -240,9 +242,10 @@ private: class LLModelPreview : public LLViewerDynamicTexture, public LLMutex -{ - public: - +{ + typedef boost::signals2::signal details_signal_t; + +public: LLModelPreview(S32 width, S32 height, LLFloater* fmp); virtual ~LLModelPreview(); @@ -275,9 +278,10 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex bool containsRiggedAsset( void ); void clearGLODGroup(); - static void textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); - + + boost::signals2::connection setDetailsCallback( const details_signal_t::slot_type& cb ){ return mDetailsSignal.connect(cb); } + protected: friend class LLFloaterModelPreview; friend class LLFloaterModelWizard; @@ -329,6 +333,8 @@ class LLModelPreview : public LLViewerDynamicTexture, public LLMutex //map of vertex buffers to models (one vertex buffer in vector per face in model std::map > > mVertexBuffer[LLModel::NUM_LODS+1]; + + details_signal_t mDetailsSignal; }; diff --git a/indra/newview/llfloatermodelwizard.cpp b/indra/newview/llfloatermodelwizard.cpp index fe53eafa40..19bdea5f44 100644 --- a/indra/newview/llfloatermodelwizard.cpp +++ b/indra/newview/llfloatermodelwizard.cpp @@ -30,6 +30,7 @@ #include "llbutton.h" #include "lldrawable.h" +#include "llcheckboxctrl.h" #include "llcombobox.h" #include "llfloater.h" #include "llfloatermodelwizard.h" @@ -71,8 +72,15 @@ void LLFloaterModelWizard::setState(int state) } } + if (state == CHOOSE_FILE) + { + getChildView("back")->setEnabled(false); + } + if (state == OPTIMIZE) { + getChildView("back")->setEnabled(true); + //mModelPreview->mModel[lod].clear(); mModelPreview->genLODs(-1); mModelPreview->mViewOption["show_physics"] = false; } @@ -423,6 +431,7 @@ BOOL LLFloaterModelWizard::postBuild() childSetValue("import_scale", (F32) 0.67335826); getChild("browse")->setCommitCallback(boost::bind(&LLFloaterModelWizard::loadModel, this)); + //getChild("lod_file")->setCommitCallback(boost::bind(&LLFloaterModelWizard::loadModel, this)); getChild("cancel")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onClickCancel, this)); getChild("close")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onClickCancel, this)); getChild("back")->setCommitCallback(boost::bind(&LLFloaterModelWizard::onClickBack, this)); @@ -441,6 +450,8 @@ BOOL LLFloaterModelWizard::postBuild() mModelPreview = new LLModelPreview(512, 512, this); mModelPreview->setPreviewTarget(16.f); + mModelPreview->setDetailsCallback(boost::bind(&LLFloaterModelWizard::setDetails, this, _1, _2, _3, _4, _5)); + center(); @@ -455,6 +466,25 @@ BOOL LLFloaterModelWizard::postBuild() return TRUE; } + +void LLFloaterModelWizard::setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost) +{ + // iterate through all the panels, setting the dimensions + for(size_t t=0; t(stateNames[t]+"_panel"); + if (panel) + { + panel->childSetText("dimension_x", llformat("%.1f", x)); + panel->childSetText("dimension_y", llformat("%.1f", y)); + panel->childSetText("dimension_z", llformat("%.1f", z)); + panel->childSetTextArg("streaming cost", "[COST]", llformat("%.3f", streaming_cost)); + panel->childSetTextArg("physics cost", "[COST]", llformat("%.3f", physics_cost)); + } + } +} + + void LLFloaterModelWizard::onUpload() { mModelPreview->rebuildUploadData(); @@ -492,8 +522,30 @@ void LLFloaterModelWizard::onPreviewLODCommit(LLUICtrl* ctrl) mModelPreview->setPreviewLOD(which_mode); } +void LLFloaterModelWizard::refresh() +{ + if (mState == CHOOSE_FILE) + { + bool model_loaded = false; + + if (mModelPreview && mModelPreview->mModelLoader && mModelPreview->mModelLoader->getLoadState() == LLModelLoader::DONE) + { + model_loaded = true; + } + + getChildView("next")->setEnabled(model_loaded); + } + if (mState == REVIEW) + { + getChildView("upload")->setEnabled(getChild("confirm_checkbox")->getValue().asBoolean()); + } + +} + void LLFloaterModelWizard::draw() { + refresh(); + LLFloater::draw(); LLRect r = getRect(); diff --git a/indra/newview/llfloatermodelwizard.h b/indra/newview/llfloatermodelwizard.h index eaf188ed40..335f4f4b56 100644 --- a/indra/newview/llfloatermodelwizard.h +++ b/indra/newview/llfloatermodelwizard.h @@ -57,13 +57,15 @@ public: virtual ~LLFloaterModelWizard(); /*virtual*/ BOOL postBuild(); void draw(); + void refresh(); BOOL handleMouseDown(S32 x, S32 y, MASK mask); BOOL handleMouseUp(S32 x, S32 y, MASK mask); BOOL handleHover(S32 x, S32 y, MASK mask); BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - + void setDetails(F32 x, F32 y, F32 z, F32 streaming_cost, F32 physics_cost); + void initDecompControls(); LLPhysicsDecomp::decomp_params mDecompParams; diff --git a/indra/newview/skins/default/xui/en/floater_model_wizard.xml b/indra/newview/skins/default/xui/en/floater_model_wizard.xml index e2ec557b06..d8492a10bb 100644 --- a/indra/newview/skins/default/xui/en/floater_model_wizard.xml +++ b/indra/newview/skins/default/xui/en/floater_model_wizard.xml @@ -190,20 +190,48 @@ Dimensions (meters): - X: [X] |Y: [Y] |Z: [Z] + X: Y: Z: + + | | + + + + ' ' ' - Resource Cost: + + + + + Resource Cost: [COST] Upload Fee: + top_pad="5" + width="130" + height="14" + left="10" + text_color="White" + word_wrap="true"> Dimensions (meters): - X: [X] |Y: [Y] |Z: [Z] + X: Y: Z: + + + | | + + + @@ -466,7 +531,7 @@ Advanced users familiar with 3d content creation tools may prefer to use the [se Recommended for solid objects Recommended for buildings Recommended for vehicles - Resource Cost: - Physics Cost: - Upload Fee: + + + + + Resource Cost: [COST] + Physics Cost: [COST] + Upload Fee: @@ -511,7 +585,7 @@ Advanced users familiar with 3d content creation tools may prefer to use the [se height="388" top_delta="0" name="review_panel" - visible="false" + visible="true" width="530" left="0"> Review the details below then click. Upload to upload your model. Your L$ balance will be charged when you click Upload. + Model Preview: - High @@ -580,32 +660,61 @@ Advanced users familiar with 3d content creation tools may prefer to use the [se height="190" follows="all" width="190"> - - - Dimensions (meters): - - - X: [X] |Y: [Y] |Z: [Z] - + + + Dimensions (meters): + + + X: Y: Z: + + + | | + + + + Resource Cost: + text_color="White">Resource Cost: [COST] This is the cost to your Region's prim/object limit, at default scale Physics Cost: + text_color="White">Physics Cost: [COST] Date: Fri, 7 Jan 2011 11:26:16 -0500 Subject: STORM-826: fix line endings and mixed tabs/spaces --- .../skins/default/xui/en/floater_web_content.xml | 380 ++++++++++----------- 1 file changed, 190 insertions(+), 190 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/floater_web_content.xml b/indra/newview/skins/default/xui/en/floater_web_content.xml index 2ad46824c2..d90d0feda9 100644 --- a/indra/newview/skins/default/xui/en/floater_web_content.xml +++ b/indra/newview/skins/default/xui/en/floater_web_content.xml @@ -1,190 +1,190 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3 From c132d20a7433e2d09e3521a15497f661fcbd18b8 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Fri, 7 Jan 2011 12:46:55 -0500 Subject: increment minor revision number to make version "2.6.0" --- indra/llcommon/llversionviewer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 356e0f4c0f..7d5afe92dc 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -28,7 +28,7 @@ #define LL_LLVERSIONVIEWER_H const S32 LL_VERSION_MAJOR = 2; -const S32 LL_VERSION_MINOR = 5; +const S32 LL_VERSION_MINOR = 6; const S32 LL_VERSION_PATCH = 0; const S32 LL_VERSION_BUILD = 0; -- cgit v1.2.3 From 8cfea0bab14afc29887de4b61350e6268b793622 Mon Sep 17 00:00:00 2001 From: Coaldust Numbers Date: Fri, 7 Jan 2011 15:08:42 -0500 Subject: VWR-1095 fix for problems with uploads following bulk upload failure de minimus contribution accepted without CA - Oz Linden --- indra/newview/llassetuploadresponders.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra') diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index f12bc16d4b..dd5bc74b2a 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -126,6 +126,7 @@ void LLAssetUploadResponder::error(U32 statusNum, const std::string& reason) break; } LLUploadDialog::modalUploadFinished(); + LLFilePicker::instance().reset(); // unlock file picker when bulk upload fails } //virtual -- cgit v1.2.3 From 6e671281a26089d7cf947236162afc8013d25ec8 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Fri, 7 Jan 2011 17:16:28 -0500 Subject: SH-759 FIXED Various crashes in model floater after you open file picker then cancel Took out erroneous logic for handling when a file picker is canceled. Reviewed by Davep. --- indra/newview/llfloatermodelpreview.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index c0b5b7cfa6..f57d524b46 100755 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -379,7 +379,8 @@ LLFloaterModelPreview::~LLFloaterModelPreview() { sInstance = NULL; - if ( mModelPreview->mModelLoader->mResetJoints ) + const LLModelLoader *model_loader = mModelPreview->mModelLoader; + if (model_loader && model_loader->mResetJoints) { gAgentAvatarp->resetJointPositions(); } @@ -2373,11 +2374,9 @@ void LLModelPreview::loadModel(std::string filename, S32 lod) LLMutexLock lock(this); - if (mModelLoader) - { - delete mModelLoader; - mModelLoader = NULL; - } + // This triggers if you bring up the file picker and then hit CANCEL. + // Just use the previous model (if any) and ignore that you brought up + // the file picker. if (filename.empty()) { @@ -2386,12 +2385,17 @@ void LLModelPreview::loadModel(std::string filename, S32 lod) // this is the initial file picking. Close the whole floater // if we don't have a base model to show for high LOD. mFMP->closeFloater(false); + mLoading = false; } - - mLoading = false; return; } + if (mModelLoader) + { + delete mModelLoader; + mModelLoader = NULL; + } + mLODFile[lod] = filename; if (lod == LLModel::LOD_HIGH) -- cgit v1.2.3 From e8423f150ffdd965da5b90ee625a8afc1519626f Mon Sep 17 00:00:00 2001 From: prep linden Date: Mon, 10 Jan 2011 11:04:13 -0500 Subject: Added support for parsing a matrix sids translation elements for joints --- indra/newview/llfloatermodelpreview.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index c0b5b7cfa6..57b648769d 100755 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1789,7 +1789,25 @@ void LLModelLoader::processJointNode( domNode* pNode, std::maptypeID() != domTranslate::ID() ) { - llwarns<< "The found element is not a translate node" <( jointResolver.getElement() ); + if ( pMatrix ) + { + //llinfos<<"A matrix SID was however found!"<getValue(); + for ( int i = 0; i < 4; i++ ) + { + for( int j = 0; j < 4; j++ ) + { + workingTransform.mMatrix[i][j] = domArray[i + j*4]; + } + } + } + else + { + llwarns<< "The found element is not translate or matrix node - most likely a corrupt export!" < Date: Mon, 10 Jan 2011 16:07:03 -0500 Subject: Fix for SH-638 --- indra/newview/llvoavatar.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index ded3e36cf6..5f0b197510 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2895,10 +2895,8 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) } LLVector3 name_position = idleUpdateNameTagPosition(root_pos_last); - mNameText->setPositionAgent(name_position); - - idleUpdateNameTagText(new_name); - + mNameText->setPositionAgent(name_position); + idleUpdateNameTagText(new_name); idleUpdateNameTagAlpha(new_name, alpha); } @@ -3184,8 +3182,9 @@ void LLVOAvatar::invalidateNameTags() if (avatar->isDead()) continue; avatar->clearNameTag(); - } - } + + } +} // Compute name tag position during idle update LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) @@ -3204,12 +3203,14 @@ LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) local_camera_up.scaleVec(mBodySize * 0.5f); local_camera_at.scaleVec(mBodySize * 0.5f); - LLVector3 name_position = mRoot.getWorldPosition() + - (local_camera_up * root_rot) - - (projected_vec(local_camera_at * root_rot, camera_to_av)); + LLVector3 name_position = mRoot.getWorldPosition(); + name_position[VZ] -= mPelvisToFoot; + name_position[VZ] += (mBodySize[VZ]* 0.5f); + name_position += (local_camera_up * root_rot) - (projected_vec(local_camera_at * root_rot, camera_to_av)); name_position += pixel_up_vec * 15.f; + return name_position; - } +} void LLVOAvatar::idleUpdateNameTagAlpha(BOOL new_name, F32 alpha) { -- cgit v1.2.3