phoenix-firestorm/indra/newview/vjfloaterlocalmesh.cpp

639 lines
15 KiB
C++

/**
* @file vjfloaterlocalmesh.cpp
* @author Vaalith Jinn
* @brief Local Mesh Floater source
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Local Mesh contribution source code
* Copyright (C) 2022, Vaalith Jinn.
*
* 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
*
* $/LicenseInfo$
*/
// linden headers
#include "llviewerprecompiledheaders.h"
#include "llfilepicker.h"
#include "llviewermenufile.h"
#include "llscrolllistctrl.h"
#include "llcheckboxctrl.h"
#include "llviewertexteditor.h"
#include "llcombobox.h"
#include "llselectmgr.h"
#include "lltoolmgr.h"
#include "lltoolcomp.h"
#include "llviewerobjectlist.h"
// own headers
#include "vjfloaterlocalmesh.h"
#include "vjlocalmesh.h"
static const S32 LOCAL_TRACKING_ID_COLUMN = 4;
/*================================*/
/* LLFloaterLocalMeshFilePicker */
/*================================*/
class LLFloaterLocalMeshFilePicker : public LLFilePickerThread
{
public:
LLFloaterLocalMeshFilePicker(LLFloaterLocalMesh* parent_floater);
virtual void notify(const std::vector<std::string>& filenames);
private:
LLFloaterLocalMesh* mParentFloater;
};
LLFloaterLocalMeshFilePicker::LLFloaterLocalMeshFilePicker(LLFloaterLocalMesh* parent_floater)
: LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA)
{
mParentFloater = parent_floater;
}
void LLFloaterLocalMeshFilePicker::notify(const std::vector<std::string>& filenames)
{
if ((!mParentFloater) || filenames.empty())
{
return;
}
mParentFloater->onBtnAddCallback(filenames[0]);
}
/*======================*/
/* LLFloaterLocalMesh */
/*======================*/
LLFloaterLocalMesh::LLFloaterLocalMesh(const LLSD & key) :
LLFloater(key)
{
mLastSelectedObject.setNull();
}
LLFloaterLocalMesh::~LLFloaterLocalMesh(void)
{
}
void LLFloaterLocalMesh::onOpen(const LLSD & key)
{
reloadFileList(false);
// register with local mesh system
LLLocalMeshSystem::getInstance()->registerFloaterPointer(this);
// toggle select tool
toggleSelectTool(true);
}
void LLFloaterLocalMesh::onClose(bool app_quitting)
{
// deregister from local mesh system
LLLocalMeshSystem::getInstance()->registerFloaterPointer(nullptr);
// toggle select tool
toggleSelectTool(false);
}
void LLFloaterLocalMesh::onSelectionChangedCallback()
{
reloadLowerUI();
}
BOOL LLFloaterLocalMesh::postBuild()
{
childSetAction("btn_add", LLFloaterLocalMesh::onBtnAdd, this);
childSetAction("btn_reload", LLFloaterLocalMesh::onBtnReload, this);
childSetAction("btn_remove", LLFloaterLocalMesh::onBtnRemove, this);
childSetAction("btn_apply", LLFloaterLocalMesh::onBtnApply, this);
childSetAction("btn_clear", LLFloaterLocalMesh::onBtnClear, this);
childSetAction("btn_showlog", LLFloaterLocalMesh::onBtnShowLog, this);
LLScrollListCtrl* scroll_ctrl = getChild<LLScrollListCtrl>("l_name_list");
if (scroll_ctrl)
{
scroll_ctrl->setCommitCallback(boost::bind(&LLFloaterLocalMesh::onFileListCommitCallback, this));
}
reloadLowerUI();
return TRUE;
}
void LLFloaterLocalMesh::draw()
{
// check if selection has changed.
LLUUID selected_id;
selected_id.setNull();
auto current_object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
if (current_object)
{
selected_id = current_object->getID();
}
if (selected_id != mLastSelectedObject)
{
mLastSelectedObject = selected_id;
onSelectionChangedCallback();
}
// continue drawing
LLFloater::draw();
}
void LLFloaterLocalMesh::onBtnAdd(void* userdata)
{
LLFloaterLocalMesh* self = (LLFloaterLocalMesh*)userdata;
(new LLFloaterLocalMeshFilePicker(self))->getFile();
}
void LLFloaterLocalMesh::onBtnAddCallback(std::string filename)
{
bool try_lods = false;
auto checkbox_use_lods = getChild<LLCheckBoxCtrl>("chkbox_use_lods");
if (checkbox_use_lods)
{
try_lods = checkbox_use_lods->get();
}
LLLocalMeshSystem::getInstance()->addFile(filename, try_lods);
}
void LLFloaterLocalMesh::onBtnReload(void* userdata)
{
LLFloaterLocalMesh* self = (LLFloaterLocalMesh*)userdata;
if (!self)
{
return;
}
LLScrollListCtrl* scroll_ctrl = self->getChild<LLScrollListCtrl>("l_name_list");
if (!scroll_ctrl)
{
return;
}
auto selected_item = scroll_ctrl->getFirstSelected();
if (!selected_item)
{
return;
}
auto selected_column = selected_item->getColumn(LOCAL_TRACKING_ID_COLUMN);
if (!selected_column)
{
return;
}
LLUUID selected_id = selected_column->getValue().asUUID();
LLLocalMeshSystem::getInstance()->reloadFile(selected_id);
}
void LLFloaterLocalMesh::onBtnRemove(void* userdata)
{
LLFloaterLocalMesh* self = (LLFloaterLocalMesh*)userdata;
if (!self)
{
return;
}
LLScrollListCtrl* scroll_ctrl = self->getChild<LLScrollListCtrl>("l_name_list");
if (!scroll_ctrl)
{
return;
}
// more checks necessary, apparently.
auto selected_item = scroll_ctrl->getFirstSelected();
if (!selected_item)
{
return;
}
auto selected_column = selected_item->getColumn(LOCAL_TRACKING_ID_COLUMN);
if (!selected_column)
{
return;
}
LLUUID selected_id = selected_column->getValue().asUUID();
LLLocalMeshSystem::getInstance()->deleteFile(selected_id);
self->reloadLowerUI();
}
void LLFloaterLocalMesh::onBtnApply(void* userdata)
{
LLFloaterLocalMesh* self = (LLFloaterLocalMesh*)userdata;
if (!self)
{
return;
}
// check scroll_ctrl pointers
auto scroll_ctrl = self->getChild<LLScrollListCtrl>("l_name_list");
if (!scroll_ctrl)
{
return;
}
auto scroll_ctrl_selected_item = scroll_ctrl->getFirstSelected();
if (!scroll_ctrl_selected_item)
{
return;
}
auto scroll_ctrl_selected_column = scroll_ctrl_selected_item->getColumn(LOCAL_TRACKING_ID_COLUMN);
if (!scroll_ctrl_selected_column)
{
return;
}
// check combobox pointer
auto objectlist_combo_box = self->getChild<LLComboBox>("object_apply_list");
if (!objectlist_combo_box)
{
return;
}
// checkbox pointer
bool use_scale = false;
auto checkbox_use_scale = self->getChild<LLCheckBoxCtrl>("chkbox_use_scale");
if (checkbox_use_scale)
{
use_scale = checkbox_use_scale->get();
}
// make sure the selection is still valid, and if so - get id.
LLUUID selected_object_id = self->getCurrentSelectionIfValid();
if (selected_object_id.isNull())
{
return;
}
// get selected local file id, object idx and use_scale boolean
LLUUID file_id = scroll_ctrl_selected_column->getValue().asUUID();
int object_idx = objectlist_combo_box->getFirstSelectedIndex();
// finally tell local mesh system to apply
LLLocalMeshSystem::getInstance()->applyVObject(selected_object_id, file_id, object_idx, use_scale);
}
void LLFloaterLocalMesh::onBtnClear(void* userdata)
{
LLFloaterLocalMesh* self = (LLFloaterLocalMesh*)userdata;
if (!self)
{
return;
}
LLUUID selected_object_id = self->getCurrentSelectionIfValid();
if (selected_object_id.isNull())
{
return;
}
LLLocalMeshSystem::getInstance()->clearVObject(selected_object_id);
}
void LLFloaterLocalMesh::onBtnShowLog(void* userdata)
{
LLFloaterLocalMesh* self = (LLFloaterLocalMesh*)userdata;
if (!self)
{
return;
}
LLScrollListCtrl* scroll_ctrl = self->getChild<LLScrollListCtrl>("l_name_list");
LLTextEditor* text_ctrl = self->getChild<LLViewerTextEditor>("text_log");
LLButton* log_btn = self->getChild<LLButton>("btn_showlog");
if ((!scroll_ctrl) || (!text_ctrl) || (!log_btn))
{
return;
}
bool scroll_visible = scroll_ctrl->getVisible();
std::string btn_text = (scroll_visible) ? "Show List" : "Show Log";
log_btn->setLabel(btn_text);
scroll_ctrl->setVisible(!scroll_visible);
text_ctrl->setVisible(scroll_visible);
if (text_ctrl->getVisible())
{
text_ctrl->clear();
// make sure something is actually selected, or we crash
auto scroll_ctrl_selected_item = scroll_ctrl->getFirstSelected();
if (!scroll_ctrl_selected_item)
{
return;
}
auto scroll_ctrl_selected_column = scroll_ctrl_selected_item->getColumn(LOCAL_TRACKING_ID_COLUMN);
if (!scroll_ctrl_selected_column)
{
return;
}
// something is selected, yay. request log
LLUUID file_id = scroll_ctrl_selected_column->getValue().asUUID();
auto log = LLLocalMeshSystem::getInstance()->getFileLog(file_id);
for (auto log_string : log)
{
text_ctrl->appendText(log_string, true);
}
}
}
void LLFloaterLocalMesh::reloadFileList(bool keep_selection)
{
LLScrollListCtrl* scroll_ctrl = getChild<LLScrollListCtrl>("l_name_list");
if (!scroll_ctrl)
{
return;
}
auto& fileinfo_vec = LLLocalMeshSystem::getInstance()->getFileInfoVector();
int selected_num = scroll_ctrl->getFirstSelectedIndex();
scroll_ctrl->clearRows();
for (auto& cinfo : fileinfo_vec)
{
std::string status_text;
switch (cinfo.mStatus)
{
case LLLocalMeshFile::LLLocalMeshFileStatus::STATUS_NONE:
{
status_text = "None";
break;
}
case LLLocalMeshFile::LLLocalMeshFileStatus::STATUS_LOADING:
{
status_text = "Loading";
break;
}
case LLLocalMeshFile::LLLocalMeshFileStatus::STATUS_ACTIVE:
{
status_text = "Active";
break;
}
case LLLocalMeshFile::LLLocalMeshFileStatus::STATUS_ERROR:
{
status_text = "Error";
break;
}
default:
{
status_text = "Error";
break;
}
}
std::string lod_state;
for (int lod_reverse_iter = 3; lod_reverse_iter >= 0; --lod_reverse_iter)
{
std::string current_state = "[";
if (cinfo.mLODAvailability[lod_reverse_iter])
{
current_state += std::to_string(lod_reverse_iter);
}
current_state += "]";
if (lod_reverse_iter > 0)
{
current_state += " ";
}
lod_state += current_state;
}
LLSD element;
element["columns"][0]["column"] = "unit_status";
element["columns"][0]["type"] = "text";
element["columns"][0]["value"] = status_text;
element["columns"][1]["column"] = "unit_name";
element["columns"][1]["type"] = "text";
element["columns"][1]["value"] = cinfo.mName;
element["columns"][2]["column"] = "unit_lods";
element["columns"][2]["type"] = "text";
element["columns"][2]["value"] = lod_state;
element["columns"][3]["column"] = "unit_objects";
element["columns"][3]["type"] = "text";
element["columns"][3]["value"] = std::to_string(cinfo.mObjectList.size());
element["columns"][4]["column"] = "unit_id_HIDDEN";
element["columns"][4]["type"] = "text";
element["columns"][4]["value"] = cinfo.mLocalID;
scroll_ctrl->addElement(element);
}
if ((keep_selection) && (selected_num >= 0) && (selected_num < scroll_ctrl->getItemCount()))
{
scroll_ctrl->selectNthItem(selected_num);
reloadLowerUI();
}
else if (scroll_ctrl->getItemCount() == 1)
{
// if we've just got the one item, select it.
// this prevents the need to explicitly select a list item when first adding a file.
scroll_ctrl->selectNthItem(0);
}
}
void LLFloaterLocalMesh::onFileListCommitCallback()
{
// i rather not register reloadLowerUI as callback, unless that's preferable?
reloadLowerUI();
}
void LLFloaterLocalMesh::reloadLowerUI()
{
// do we have a valid target selected?
bool selected_target_valid = !getCurrentSelectionIfValid().isNull();
// do we have a valid file in list and selected?
bool selected_file_loaded = false;
bool selected_file_active = false;
LLUUID selected_file_id;
selected_file_id.setNull();
auto scroll_ctrl = getChild<LLScrollListCtrl>("l_name_list");
if (scroll_ctrl)
{
auto selected_item = scroll_ctrl->getFirstSelected();
if (selected_item)
{
auto selected_column = selected_item->getColumn(LOCAL_TRACKING_ID_COLUMN);
if (selected_column)
{
selected_file_id = selected_column->getValue().asUUID();
}
}
}
std::vector<std::string> selected_object_list;
if (!selected_file_id.isNull())
{
auto& fileinfo_vector = LLLocalMeshSystem::getInstance()->getFileInfoVector();
for (auto fileinfo : fileinfo_vector)
{
if (selected_file_id == fileinfo.mLocalID)
{
switch (fileinfo.mStatus)
{
case LLLocalMeshFile::STATUS_ACTIVE:
{
selected_file_loaded = true;
selected_file_active = true;
selected_object_list = fileinfo.mObjectList;
break;
}
case LLLocalMeshFile::STATUS_ERROR:
{
selected_file_loaded = true;
break;
}
default:
{
// no other status enables anything.
break;
}
}
break;
}
}
}
// checks done
// toggle target-relevant elements
auto btn_clear = getChild<LLButton>("btn_clear");
if (btn_clear)
{
btn_clear->setEnabled(selected_target_valid);
}
// toggle elements that need a file to be loaded, even if it's got an error
auto btn_remove = getChild<LLButton>("btn_remove");
if (btn_remove)
{
btn_remove->setEnabled(selected_file_loaded);
}
auto btn_reload = getChild<LLButton>("btn_reload");
if (btn_reload)
{
btn_reload->setEnabled(selected_file_loaded);
}
// apply needs a target to be selected AND a file to be loaded and active
auto btn_apply = getChild<LLButton>("btn_apply");
if (btn_apply)
{
btn_apply->setEnabled((selected_target_valid) && (selected_file_active));
}
// objectlist needs the file to be loaded and active
auto objectlist_combo_box = getChild<LLComboBox>("object_apply_list");
if (objectlist_combo_box)
{
objectlist_combo_box->setEnabled(selected_file_active);
objectlist_combo_box->clearRows();
// and if it is loaded & active, fill object list
if ((selected_file_active) && (!selected_object_list.empty()))
{
for (auto object_name : selected_object_list)
{
objectlist_combo_box->addSimpleElement(object_name);
}
objectlist_combo_box->selectFirstItem();
}
}
}
void LLFloaterLocalMesh::toggleSelectTool(bool toggle)
{
// hiding this in it's own function so i can experiment with the ux of
// toggling it in either onOpen/onClose, or onFocusReceived/onFocusLost
if (toggle)
{
LLSelectMgr::getInstance()->setForceSelection(TRUE);
LLToolMgr::getInstance()->setTransientTool(LLToolCompInspect::getInstance());
// this one's necessary for the tool to actually be active, go figure.
mObjectSelection = LLSelectMgr::getInstance()->getSelection();
}
else
{
if (LLToolMgr::getInstance()->getBaseTool() == LLToolCompInspect::getInstance())
{
LLToolMgr::getInstance()->clearTransientTool();
}
LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset);
}
}
LLUUID LLFloaterLocalMesh::getCurrentSelectionIfValid()
{
LLUUID result;
result.setNull();
auto current_object = gObjectList.findObject(mLastSelectedObject);
if (!current_object)
{
return result;
}
// turning a non-mesh object into mesh is immensely more hacky, let's not do that.
if (!current_object->isMesh())
{
return result;
}
// any other rules can be set here
result = mLastSelectedObject;
return result;
}