Merge integration of Vaalith's Local_mesh to main

Initial release of local mesh support.
Local mesh implementation by Vaalith Jinn
master
Beq 2022-09-16 01:21:27 +01:00
commit d31a2dacba
15 changed files with 4220 additions and 0 deletions

View File

@ -222,6 +222,11 @@ set(viewer_SOURCE_FILES
# <FS:Ansariel> [Legacy Bake]
llagentwearablesfetch.cpp
# local mesh
vjlocalmesh.cpp
vjfloaterlocalmesh.cpp
vjlocalmeshimportdae.cpp
llaccountingcostmanager.cpp
llaisapi.cpp
llagent.cpp
@ -996,6 +1001,11 @@ set(viewer_HEADER_FILES
# <FS:Ansariel> [Legacy Bake]
llagentwearablesfetch.h
# local mesh
vjlocalmesh.h
vjfloaterlocalmesh.h
vjlocalmeshimportdae.h
llaccountingcostmanager.h
llaisapi.h
llagent.h

View File

@ -26247,5 +26247,16 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>-4</integer>
</map>
<key>FSLocalMeshScaleAlwaysMeters</key>
<map>
<key>Comment</key>
<string>Ignore the units specified by the Collada file. Useful when importing from tools such as Maya</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
</map>
</llsd>

View File

@ -221,6 +221,9 @@
#include "NACLfloaterexploresounds.h"
#include "particleeditor.h"
#include "quickprefs.h"
#include "vjfloaterlocalmesh.h" // local mesh
// handle secondlife:///app/openfloater/{NAME} URLs
class LLFloaterOpenHandler : public LLCommandHandler
{
@ -521,6 +524,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("script_recover", "floater_script_recover.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptRecover>);
LLFloaterReg::add("sound_explorer", "floater_NACL_explore_sounds.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<NACLFloaterExploreSounds>);
LLFloaterReg::add("vram_usage", "floater_fs_vram_usage.xml", static_cast<LLFloaterBuildFunc>( &LLFloaterReg::build< FSFloaterVRAMUsage >) );
LLFloaterReg::add("local_mesh_floater", "floater_vj_local_mesh.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLocalMesh>); // local mesh
LLFloaterReg::registerControlVariables(); // Make sure visibility and rect controls get preserved when saving
}

View File

@ -2449,6 +2449,24 @@ S32 LLViewerObjectList::findReferences(LLDrawable *drawablep) const
return num_refs;
}
std::vector<LLUUID> LLViewerObjectList::findMeshObjectsBySculptID(LLUUID target_sculpt_id)
{
std::vector<LLUUID> result;
// getting IDs rather than object/vovobject pointers here because
// of the extra safety if later calling them through findObject
for (auto current_object : mObjects)
{
if ((current_object->isMesh()) &&
(current_object->getVolume()) &&
(current_object->getVolume()->getParams().getSculptID() == target_sculpt_id))
{
result.push_back(current_object->getID());
}
}
return result;
}
void LLViewerObjectList::orphanize(LLViewerObject *childp, U32 parent_id, U32 ip, U32 port)
{

View File

@ -159,6 +159,7 @@ public:
void cleanupReferences(LLViewerObject *objectp);
S32 findReferences(LLDrawable *drawablep) const; // Find references to drawable in all objects, and return value.
std::vector<LLUUID> findMeshObjectsBySculptID(LLUUID target_sculpt_id);
S32 getOrphanParentCount() const { return (S32) mOrphanParents.size(); }
S32 getOrphanCount() const { return mNumOrphans; }

View File

@ -252,6 +252,8 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
mMDCImplCount = 0;
mLastRiggingInfoLOD = -1;
mResetDebugText = false;
mIsLocalMesh = false;
mIsLocalMeshUsingScale = false;
}
LLVOVolume::~LLVOVolume()
@ -345,6 +347,14 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
bool enfore_strict_object_check = LLGridManager::instance().isInSecondLife() && fsEnforceStrictObjectCheck;
// </FS:Ansariel>
// local mesh begin
// rationale: we don't want server updates for a local object, cause the server tends to override things.
if (mIsLocalMesh)
{
return 0;
}
// local mesh end
LLColor4U color;
const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA);
const bool previously_volume_changed = mVolumeChanged;

View File

@ -316,6 +316,8 @@ public:
//virtual
void updateRiggingInfo();
S32 mLastRiggingInfoLOD;
bool mIsLocalMesh;
bool mIsLocalMeshUsingScale;
// Functions that deal with media, or media navigation

View File

@ -0,0 +1,317 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater name="Local Mesh"
positioning="centered"
legacy_header_height="18"
can_resize="false"
can_dock="false"
can_close="true"
height="300"
width="505"
min_width="505"
min_height="300"
layout="topleft"
save_rect="true"
save_visibility="true"
single_instance="false"
save_dock_state="false"
title="Local Mesh">
<tab_container name="local_mesh_tabs"
follows="top|left"
top="20"
left="0"
height="223"
width="500"
tab_position="top"
enable_tabs_flashing="true"
tabs_flashing_color="MenuItemFlashBgColor">
<last_tab tab_top_image_flash="TabTop_Right_Flashing" />
<panel name="local_mesh_assets_panel"
help_topic="add_local_mesh"
label="Local Mesh Assets"
layout="topleft"
title="Local Mesh Assets">
<fs_scroll_list name="l_name_list"
left_delta="5"
top_pad="5"
width="495"
height="220"
content_type="Objects"
follows="left|top|right|bottom"
column_padding="0"
draw_heading="true"
multi_select="true"
search_column="1"
visible="true">
<column name="unit_status"
label="Status"
width="55" />
<column name="unit_name"
label="Name"
dynamicwidth="true" />
<column name="unit_lods"
label="LODs"
width="55" />
<column name="unit_objects"
label="Objects"
width="55" />
<column name="unit_id_HIDDEN"
label="ID"
width="0" />
</fs_scroll_list>
</panel>
<panel name="logs_panel"
label="Log"
layout="topleft"
help_topic="text_log">
<view_border name="local_mesh_log_tab_border"
bevel_style="none"
follows="top|left"
height="200"
layout="topleft"
left="3"
ignore_tab="false"
right="-2"
top_pad="0"/>
<text_editor name="local_mesh_log"
max_length="65536"
left_delta="0"
top_delta="0"
right="-2"
height="195"
follows="left|top|right|bottom"
type="string"
embedded_items="false"
font="SansSerif"
ignore_tab="false"
layout="topleft"
parse_urls="false"
spellcheck="false"
read_only="true"
word_wrap="true">
</text_editor>
</panel>
<panel name="local_mesh_settings_panel"
layout="topleft"
label="Settings"
help_topic="local_mesh_settings_prefs">
<view_border name="local_mesh_settings_tab_border"
bevel_style="none"
follows="top|left"
height="200"
layout="topleft"
left="3"
ignore_tab="false"
right="-2"
top_pad="0"
width="619" />
<check_box name="local_mesh_scale_use_meters"
height="15"
control_name="FSLocalMeshScaleAlwaysMeters"
follows="top|left"
top="8"
left="8"
width="300"
label="Assume scale is in meters."
tool_tip="Ignore the cm scale units used by tools such as Maya" />
<text name="lod_suffix_label"
follows="left|top"
layout="topleft"
left="8"
height="12"
top_pad="5"
width="110">
LOD Suffixes:
</text>
<combo_box name="lod_suffix_combo"
height="18"
follows="left|top"
width="180"
top_pad="5"
tool_tip="Choose a standard or manually edit...
SL default - (Lowest is LOD0, High has no suffix).
Game Engine - Unity/UE5, etc. (Lowest=LOD3, High=LOD0).
LOD Names - English LOD names, (Lowest='LOWEST', High='HIGH').">
<combo_item name="choose_one">Current</combo_item>
<combo_item name="suff_sl">SL Standard</combo_item>
<combo_item name="suff_unity">Games Engine Standard</combo_item>
<combo_item name="suff_descriptive">LOD Names</combo_item>
</combo_box>
<text name="suf_lowest_lab"
follows="left|top"
layout="topleft"
left="8"
height="12"
top_pad="10"
halign="right"
width="60">
Lowest:
</text>
<line_editor name="suf_lowest"
control_name="FSMeshLowestLodSuffix"
border_style="line"
border_thickness="1"
follows="left|top"
font="SansSerif"
height="23"
top_delta="-5"
layout="topleft"
left_pad="5"
max_length_chars="10"
tool_tip="The Suffix used to identify the Lowest LOD file on disk and models within a file."
width="60" />
<text name="suf_low_lab"
follows="left|top"
layout="topleft"
halign="right"
height="12"
left="8"
top_pad="5"
width="60">
Low:
</text>
<line_editor name="suf_low"
control_name="FSMeshLowLodSuffix"
border_style="line"
border_thickness="1"
follows="left|top"
font="SansSerif"
height="23"
top_delta="-5"
layout="topleft"
left_pad="5"
max_length_chars="10"
tool_tip="The Suffix used to identify the Low LOD file on disk and models within a file."
width="60" />
<text name="suf_medium_lab"
follows="left|top"
top_pad="5"
left="8"
layout="topleft"
height="12"
halign="right"
width="60">
Medium:
</text>
<line_editor name="suf_medium"
control_name="FSMeshMediumLodSuffix"
border_style="line"
border_thickness="1"
follows="left|top"
font="SansSerif"
height="23"
top_delta="-5"
layout="topleft"
left_pad="5"
max_length_chars="10"
tool_tip="The Suffix used to identify the Medium LOD file on disk and models within a file."
width="60" />
<text name="suf_high_lab"
follows="left|top"
layout="topleft"
height="12"
halign="right"
top_pad="5"
left="8"
width="60">
High:
</text>
<line_editor name="suf_high"
control_name="FSMeshHighLodSuffix"
border_style="line"
border_thickness="1"
follows="left|top"
font="SansSerif"
height="23"
top_delta="-5"
layout="topleft"
left_pad="5"
max_length_chars="10"
tool_tip="The Suffix used to identify the High LOD file on disk and models within a file."
width="60" />
<text name="suf_physics_lab"
follows="left|top"
left="8"
layout="topleft"
height="12"
halign="right"
top_pad="5"
width="60">
Physics:
</text>
<line_editor name="suf_physics"
control_name="FSMeshPhysicsSuffix"
border_style="line"
border_thickness="1"
follows="left|top"
font="SansSerif"
height="23"
top_delta="-5"
layout="topleft"
left_pad="5"
max_length_chars="10"
tool_tip="The Suffix used to identify the Physics file on disk and models within a file."
width="60" />
</panel>
</tab_container>
<button name="btn_apply"
height="18"
label="Apply"
layout="topleft"
left="10"
top="253"
width="90"
visible="true" />
<combo_box name="object_apply_list"
height="18"
layout="topleft"
follows="left|top|bottom"
left="105"
top="253"
width="315"
visible="true">
/>
</combo_box>
<button name="btn_add"
height="18"
label="Add"
layout="topleft"
left="10"
top="275"
width="90"
visible="true" />
<button name="btn_remove"
height="18"
label="Remove"
layout="topleft"
left="105"
top="275"
width="90"
visible="true" />
<button name="btn_reload"
height="18"
label="Reload"
layout="topleft"
left="200"
top="275"
width="90"
visible="true" />
<button name="btn_clear"
height="18"
label="Clear"
layout="topleft"
left="295"
top="275"
width="90"
visible="true" />
<button name="btn_rez"
height="18"
label="Rez Selected"
tool_tip="Create a donor mesh inworld and populate with selected local mesh. This creates an empty object in-world."
layout="topleft"
left="390"
top="275"
width="90"
visible="true" />
</floater>

View File

@ -2215,6 +2215,16 @@
parameter="" />
</menu_item_call>
</menu>
<!-- local mesh begin -->
<menu_item_call
enabled="true"
label="Local Mesh"
name="local_mesh">
<menu_item_call.on_click function="Floater.Show" parameter="local_mesh_floater"/>
</menu_item_call>
<!-- local mesh end -->
<menu_item_separator/>
<menu_item_call
enabled="false"

View File

@ -0,0 +1,730 @@
/**
* @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 "llinventoryicon.h"
#include "llviewercontrol.h"
#include "llviewermenufile.h"
#include "fsscrolllistctrl.h"
#include "llcheckboxctrl.h"
#include "llviewertexteditor.h"
#include "llcombobox.h"
#include "llselectmgr.h"
#include "lltoolmgr.h"
#include "lltoolcomp.h"
#include "llmodelpreview.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:
explicit LLFloaterLocalMeshFilePicker(LLFloaterLocalMesh* parent_floater);
void notify(const std::vector<std::string>& filenames) final;
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) = default;
//static
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();
showLog();
}
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_rez", LLFloaterLocalMesh::onBtnRez, this);
mTabContainer = findChild<LLTabContainer>("local_mesh_tabs");
if(mTabContainer)
{
mLogPanel = mTabContainer->getChild<LLViewerTextEditor>("local_mesh_log");
mScrollCtrl = mTabContainer->getChild<FSScrollListCtrl>("l_name_list");
mScrollCtrl->setCommitCallback(boost::bind(&LLFloaterLocalMesh::onFileListCommitCallback, this));
// mTabContainer->setCommitCallback(boost::bind(&LLFloaterLocalMesh::onTabChange, this));
}
getChild<LLComboBox>("lod_suffix_combo")->setCommitCallback(boost::bind(&LLFloaterLocalMesh::onSuffixStandardSelected, this, (LLUICtrl*)this));
reloadLowerUI();
return TRUE;
}
void LLFloaterLocalMesh::update_selected_target(LLUUID selected_id)
{
if ( selected_id != mLastSelectedObject )
{
mLastSelectedObject = selected_id;
onSelectionChangedCallback();
}
}
void LLFloaterLocalMesh::draw()
{
// check if selection has changed.
if (auto current_object = LLSelectMgr::getInstance()->getSelection()->getFirstObject())
{
update_selected_target( current_object->getID() );
}
else
{
update_selected_target( LLUUID::null );
}
// continue drawing
LLFloater::draw();
}
void LLFloaterLocalMesh::onBtnAdd(void* userdata)
{
auto* self = (LLFloaterLocalMesh*)userdata;
(new LLFloaterLocalMeshFilePicker(self))->getFile();
}
void LLFloaterLocalMesh::onBtnAddCallback(std::string filename)
{
static const bool try_lods {true};
LLLocalMeshSystem::getInstance()->addFile(filename, try_lods);
showLog();
}
void LLFloaterLocalMesh::onBtnReload(void* userdata)
{
auto* self = static_cast<LLFloaterLocalMesh*>(userdata);
if (!self)
{
return;
}
auto selected_item = self->mScrollCtrl->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)
{
auto* self = static_cast<LLFloaterLocalMesh*>(userdata);
if (!self)
{
return;
}
// more checks necessary, apparently.
auto selected_item = self->mScrollCtrl->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)
{
auto* self = static_cast<LLFloaterLocalMesh*>(userdata);
if (!self)
{
return;
}
auto scroll_ctrl_selected_item = self->mScrollCtrl->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;
}
// 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, true);
}
//static
void LLFloaterLocalMesh::onSuffixStandardSelected(LLUICtrl* ctrl, void* userdata)
{
S32 which{0};
// SL standard LODs are the reverse of every other game engine (LOD0 least detail)
// SL has no suffix for the HIGH LOD
static const std::array<std::string,5> sl_suffixes = {
"LOD0",
"LOD1",
"LOD2",
"",
"PHYS"
};
// Game engines (UE, Unity, CryEngine, Godot, etc.) all use LOD0 as highest.
// They typically also label the high with a suffix too
static const std::array<std::string,5> std_suffixes = {
"LOD3",
"LOD2",
"LOD1",
"LOD0",
"PHYS"
};
// Human friendly. When making things manually people naturally use names.
static const std::array<std::string,5> desc_suffixes = {
"LOWEST",
"LOW",
"MED",
"HIGH",
"PHYS"
};
auto * self = (LLFloaterLocalMesh *)ctrl;
if (LLCtrlSelectionInterface* iface = self->childGetSelectionInterface("lod_suffix_combo"))
{
which = iface->getFirstSelectedIndex();
}
else
{
LL_WARNS() << "no UI element found! nothing changed" << LL_ENDL;
return;
}
gSavedSettings.setS32("FSMeshLodSuffixScheme",which);
switch (which)
{
case 1: // SL
for (int i = 0; i < LLModel::NUM_LODS; i++)
{
gSavedSettings.setString(LLModelPreview::sSuffixVarNames[i], sl_suffixes[i]);
}
break;
case 2: // standard
for (int i = 0; i < LLModel::NUM_LODS; i++)
{
gSavedSettings.setString(LLModelPreview::sSuffixVarNames[i], std_suffixes[i]);
}
break;
case 3: // descriptive english
for (int i = 0; i < LLModel::NUM_LODS; i++)
{
gSavedSettings.setString(LLModelPreview::sSuffixVarNames[i], desc_suffixes[i]);
}
break;
default:
LL_WARNS() << "no standard selected, nothing changed" << LL_ENDL;
break;
};
}
void LLFloaterLocalMesh::onBtnClear(void* userdata)
{
auto* self = static_cast<LLFloaterLocalMesh*>(userdata);
if (!self)
{
return;
}
LLUUID selected_object_id = self->getCurrentSelectionIfValid();
if (selected_object_id.isNull())
{
return;
}
LLLocalMeshSystem::getInstance()->clearVObject(selected_object_id);
}
bool LLFloaterLocalMesh::processPrimCreated(LLViewerObject* object)
{
if(!object)
{
return false;
}
// Select the new object
LLSelectMgr::getInstance()->selectObjectAndFamily(object, TRUE);
update_selected_target( object->getID() );
// LLUUID local_id{"aee92334-90e9-110b-7c03-0ff3bc19de63"};
LLUUID local_id{};
auto* volp = object->getVolume();
if(!volp)
{
return false;
}
auto volume_params{volp->getParams()};
object->setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, TRUE, TRUE);
auto *sculpt_params = (LLSculptParams *)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
if (sculpt_params)
{
sculpt_params->setSculptTexture( local_id, LL_SCULPT_TYPE_MESH);
volume_params.setSculptID(local_id, LL_SCULPT_TYPE_MESH);
object->updateVolume(volume_params);
}
else
{
return false;
}
auto scroll_ctrl_selected_item = this->mScrollCtrl->getFirstSelected();
if(!scroll_ctrl_selected_item){return true;}; // at this point we have a valid object even if we can't fill it.
auto scroll_ctrl_selected_column = scroll_ctrl_selected_item->getColumn(LOCAL_TRACKING_ID_COLUMN);
if(!scroll_ctrl_selected_column){return true;}; // at this point we have a valid object even if we can't fill it.
auto objectlist_combo_box = this->getChild<LLComboBox>("object_apply_list");
if(!objectlist_combo_box){return true;}; // at this point we have a valid object even if we can't fill it.
// TODO: replace this with check box. "apply selected"
bool apply_local { scroll_ctrl_selected_item && scroll_ctrl_selected_column && objectlist_combo_box };
if ( apply_local )
{
local_id = scroll_ctrl_selected_column->getValue().asUUID();
// fill it up with local goodness
static const bool use_scale {true};
// // make sure the selection is still valid, and if so - get id.
// get selected local file id, object idx and use_scale boolean
int object_idx = objectlist_combo_box->getFirstSelectedIndex();
LLLocalMeshSystem::getInstance()->applyVObject(object->getID(), local_id, object_idx, use_scale);
volp = object->getVolume();
if(!volp)
{
return true;
}
volume_params = volp->getParams();
object->updateVolume(volume_params);
object->markForUpdate(true);
}
return true;
}
void LLFloaterLocalMesh::onBtnRez(void* userdata)
{
auto* self = (LLFloaterLocalMesh*)userdata;
self->mObjectCreatedCallback = gObjectList.setNewObjectCallback(boost::bind(&LLFloaterLocalMesh::processPrimCreated, self, _1));
LLToolMgr::getInstance()->getCurrentToolset()->selectTool( (LLTool *) LLToolCompCreate::getInstance());
}
void LLFloaterLocalMesh::showLog()
{
// load a log file for the newly selected item
mLogPanel->clear();
// make sure something is actually selected, or we crash
auto scroll_ctrl_selected_item = mScrollCtrl->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)
{
mLogPanel->appendText(log_string, true);
}
}
void LLFloaterLocalMesh::reloadFileList(bool keep_selection)
{
auto& fileinfo_vec = LLLocalMeshSystem::getInstance()->getFileInfoVector();
int selected_num = mScrollCtrl->getFirstSelectedIndex();
mScrollCtrl->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;
}
std::string icon_name = LLInventoryIcon::getIconName(
LLAssetType::AT_OBJECT,
LLInventoryType::IT_OBJECT);
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;
element["value"] = "fcff33f4-82b7-367a-632e-1673b7142982";
mScrollCtrl->addElement(element);
}
if ( keep_selection && (selected_num >= 0) && (selected_num < mScrollCtrl->getItemCount()) )
{
mScrollCtrl->selectNthItem(selected_num);
reloadLowerUI();
}
else if (mScrollCtrl->getItemCount() > 0)
{
// select the last item in the list
mScrollCtrl->selectNthItem( mScrollCtrl->getItemCount()-1 );
reloadLowerUI();
}
}
void LLFloaterLocalMesh::onFileListCommitCallback()
{
reloadLowerUI();
showLog();
}
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();
if (auto selected_item = mScrollCtrl->getFirstSelected())
{
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() const
{
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;
}

View File

@ -0,0 +1,88 @@
/**
* @file vjfloaterlocalmesh.h
* @author Vaalith Jinn
* @brief Local Mesh Floater header
*
* $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$
*/
#pragma once
#include "llfloater.h"
#include "lltabcontainer.h"
class LLObjectSelection;
class LLFloaterLocalMeshFilePicker;
class LLFloaterLocalMesh : public LLFloater
{
public:
explicit LLFloaterLocalMesh(const LLSD& key);
~LLFloaterLocalMesh(void) final;
void onOpen(const LLSD& key) final;
void onClose(bool app_quitting) final;
void onSelectionChangedCallback();
BOOL postBuild() final;
void draw() final;
/* add - loads a new file, adds it to the list and reads it.
reload - re-loads a selected file, reapplies it to viewer objects.
remove - clears all affected viewer objects and unloads selected file
apply - applies selected file onto a selected viewer object
clear - reverts a selected viewer object to it's normal state
show log/show list - toggles between loaded file list, and log.
*/
static void onBtnAdd(void* userdata);
void onBtnAddCallback(std::string filename);
static void onBtnReload(void* userdata);
static void onBtnRemove(void* userdata);
static void onBtnApply(void* userdata);
static void onBtnClear(void* userdata);
static void onBtnRez(void* userdata);
static void onSuffixStandardSelected(LLUICtrl* ctrl, void *userdata);
bool processPrimCreated(LLViewerObject* object);
void reloadFileList(bool keep_selection);
void onFileListCommitCallback();
void reloadLowerUI();
void toggleSelectTool(bool toggle);
LLUUID getCurrentSelectionIfValid() const;
private:
void update_selected_target(LLUUID selected_id);
boost::signals2::connection mObjectCreatedCallback;
void showLog();
LLTabContainer * mTabContainer;
LLTextEditor * mLogPanel;
LLScrollListCtrl * mScrollCtrl;
// llsafehandle is deprecated.
LLPointer<LLObjectSelection> mObjectSelection;
// since we use this to check if selection changed,
// and since uuid seems like a safer way to check rather than
// comparing llvovolumes, we might as well refer to this
// when querying what is actually selected.
LLUUID mLastSelectedObject;
};

File diff suppressed because it is too large Load Diff

255
indra/newview/vjlocalmesh.h Normal file
View File

@ -0,0 +1,255 @@
/**
* @file vjlocalmesh.h
* @author Vaalith Jinn
* @brief Local Mesh main mechanism header
*
* $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$
*/
#pragma once
// linden headers
#include "llmodel.h" // LLMeshSkinInfo
// STL headers
#include <future>
class LLFloaterLocalMesh;
enum LLLocalMeshFileLOD
{
LOCAL_LOD_LOWEST,
LOCAL_LOD_LOW,
LOCAL_LOD_MEDIUM,
LOCAL_LOD_HIGH,
LOCAL_NUM_LODS
};
/*==========================================*/
/* LLLocalMeshFace: aka submesh denoted by */
/* material assignment. holds per-face */
/* values for indices, bounding box and */
/* vtx pos, normals, uv coords, weights. */
/*==========================================*/
class LLLocalMeshFace
{
public:
struct LLLocalMeshSkinUnit
{
std::array<int, 4> mJointIndices;
std::array<float, 4> mJointWeights;
};
void setFaceBoundingBox(LLVector4 data_in, bool initial_values = false);
int getNumVerts() const { return mPositions.size(); }
int getNumIndices() const { return mIndices.size(); }
std::vector<int>& getIndices() { return mIndices; };
std::vector<LLVector4>& getPositions() { return mPositions; };
std::vector<LLVector4>& getNormals() { return mNormals; };
std::vector<LLVector2>& getUVs() { return mUVs; };
std::vector<LLLocalMeshSkinUnit>& getSkin() { return mSkin; }
std::pair<LLVector4, LLVector4>& getFaceBoundingBox() { return mFaceBoundingBox; }
private:
std::vector<int> mIndices;
std::vector<LLVector4> mPositions;
std::vector<LLVector4> mNormals;
std::vector<LLVector2> mUVs;
std::vector<LLLocalMeshSkinUnit> mSkin;
std::pair<LLVector4, LLVector4> mFaceBoundingBox;
};
/*==========================================*/
/* LLLocalMeshObject: collection of faces */
/* has object name, transform & skininfo, */
/* volumeid and volumeparams for vobj. */
/* when applied - fills vobj volume. */
/*==========================================*/
class LLLocalMeshObject
{
public:
// life cycle management
explicit LLLocalMeshObject(std::string_view name);
~LLLocalMeshObject();
// translation and scale
void computeObjectBoundingBox();
void computeObjectTransform(const LLMatrix4& scene_transform);
void normalizeFaceValues(LLLocalMeshFileLOD lod_iter);
// applying local object to viewer object
void fillVolume(LLLocalMeshFileLOD lod);
void attachSkinInfo();
// getters
std::vector<std::unique_ptr<LLLocalMeshFace>>& getFaces(LLLocalMeshFileLOD lod) { return mFaces[lod]; };
std::pair<LLVector4, LLVector4>& getObjectBoundingBox() { return mObjectBoundingBox; };
LLVector4 getObjectTranslation() const { return mObjectTranslation; };
std::string getObjectName() const { return mObjectName; };
LLVector4 getObjectSize() const { return mObjectSize; };
LLVector4 getObjectScale() const { return mObjectScale; };
LLMeshSkinInfo& getObjectMeshSkinInfo() { return mMeshSkinInfo; };
LLVolumeParams getVolumeParams() const { return mVolumeParams; };
bool getIsRiggedObject() const;
private:
// internal data keeping
std::array<std::vector<std::unique_ptr<LLLocalMeshFace>>, 4> mFaces;
std::pair<LLVector4, LLVector4> mObjectBoundingBox;
std::string mObjectName;
LLVector4 mObjectTranslation;
LLVector4 mObjectSize;
LLVector4 mObjectScale;
// vovolume
LLMeshSkinInfo mMeshSkinInfo;
LLUUID mSculptID;
LLVolumeParams mVolumeParams;
};
/*==========================================*/
/* LLLocalMeshFile: Single Unit */
/* owns filenames [main and lods] */
/* owns the loaded local mesh objects */
/*==========================================*/
class LLLocalMeshFile
{
// class specific types
public:
enum LLLocalMeshFileStatus
{
STATUS_NONE,
STATUS_LOADING,
STATUS_ACTIVE,
STATUS_ERROR
};
// for future gltf support, possibly more.
enum class LLLocalMeshFileExtension
{
EXTEN_DAE,
EXTEN_NONE
};
struct LLLocalMeshFileInfo
{
std::string mName;
LLLocalMeshFileStatus mStatus;
LLUUID mLocalID;
std::array<bool, 4> mLODAvailability;
std::vector<std::string> mObjectList;
};
struct LLLocalMeshLoaderReply
{
bool mChanged;
std::vector<std::string> mLog;
std::array<bool, 4> mStatus;
};
// life cycle management
LLLocalMeshFile(const std::string& filename, bool try_lods);
~LLLocalMeshFile();
// disallowing copy
LLLocalMeshFile(const LLLocalMeshFile& local_mesh_file) = delete;
LLLocalMeshFile& operator=(const LLLocalMeshFile& local_mesh_file) = delete;
// file loading
void reloadLocalMeshObjects(bool initial_load = false);
LLLocalMeshFileStatus reloadLocalMeshObjectsCheck();
void reloadLocalMeshObjectsCallback();
bool updateLastModified(LLLocalMeshFileLOD lod);
std::vector<std::unique_ptr<LLLocalMeshObject>>& getObjectVector() { return mLoadedObjectList; };
// info getters
bool notifyNeedsUIUpdate();
LLLocalMeshFileInfo getFileInfo();
std::string getFilename(LLLocalMeshFileLOD lod) const { return mFilenames[lod]; };
LLUUID getFileID() const { return mLocalMeshFileID; };
std::vector<std::string> getFileLog() const { return mLoadingLog; };
// viewer object
void updateVObjects();
void applyToVObject(LLUUID viewer_object_id, int object_index, bool use_scale);
// misc
void pushLog(const std::string& who, const std::string& what, bool is_error = false);
private:
std::array<std::string, LOCAL_NUM_LODS> mFilenames;
std::array<LLSD, LOCAL_NUM_LODS> mLastModified;
std::array<bool, LOCAL_NUM_LODS> mLoadedSuccessfully;
bool mTryLODFiles;
std::string mShortName;
std::vector<std::string> mLoadingLog;
LLLocalMeshFileExtension mExtension;
LLLocalMeshFileStatus mLocalMeshFileStatus;
LLUUID mLocalMeshFileID;
bool mLocalMeshFileNeedsUIUpdate;
std::future<LLLocalMeshLoaderReply> mAsyncFuture;
std::vector<std::unique_ptr<LLLocalMeshObject>> mLoadedObjectList;
std::vector<LLUUID> mSavedObjectSculptIDs;
};
/*=============================*/
/* LLLocalMeshSystem: */
/* user facing manager class. */
/*=============================*/
class LLLocalMeshSystem : public LLSingleton<LLLocalMeshSystem>
{
// life cycle management
LLSINGLETON(LLLocalMeshSystem);
public:
~LLLocalMeshSystem();
// file management
void addFile(const std::string& filename, bool try_lods);
void deleteFile(LLUUID local_file_id);
void reloadFile(LLUUID local_file_id);
// viewer object management
void applyVObject(LLUUID viewer_object_id, LLUUID local_file_id, int object_index, bool use_scale);
void clearVObject(LLUUID viewer_object_id);
// high level async support
void triggerCheckFileAsyncStatus();
void checkFileAsyncStatus();
// floater two-way communication
void registerFloaterPointer(LLFloaterLocalMesh* floater_ptr);
void triggerFloaterRefresh( bool keep_selection=true );
std::vector<LLLocalMeshFile::LLLocalMeshFileInfo> getFileInfoVector() const;
std::vector<std::string> getFileLog(LLUUID local_file_id) const;
private:
std::vector<std::unique_ptr<LLLocalMeshFile>> mLoadedFileList;
bool mFileAsyncsOngoing;
LLFloaterLocalMesh* mFloaterPtr;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
/**
* @file vjlocalmeshimportdae.h
* @author Vaalith Jinn
* @brief Local Mesh dae importer header
*
* $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$
*/
#pragma once
// linden headers
#include "llviewerprecompiledheaders.h"
// formal declarations
class LLLocalMeshObject;
class LLLocalMeshFace;
class LLLocalMeshFileData;
// collada dom magic
#if LL_MSVC
#pragma warning (disable : 4263)
#pragma warning (disable : 4264)
#endif
#include "dom/domMesh.h"
#if LL_MSVC
#pragma warning (default : 4263)
#pragma warning (default : 4264)
#endif
/*
NOTE: basically everything here is just a refactor of lldaeloader
in what is hopefully more modernized and easier to understand c++
and more importantly - less dependent on the caller.
there's only so much of a wheel you can reinvent.
one important note: i explicitly excluded the parts that add
additional faces (on too many verts) and additional objects (on too many faces)
because that perpetuates bad habits in bad creators.
if someone wants to upload a mesh object with 400 faces -
let them do it by hand, or eat cake, or something.
*/
class LLLocalMeshImportDAE
{
public:
// use default constructor/destructor if we can get away with it.
public:
typedef std::pair<bool, std::vector<std::string>> loadFile_return;
loadFile_return loadFile(LLLocalMeshFile* data, LLLocalMeshFileLOD lod);
bool processObject(domMesh* current_mesh, LLLocalMeshObject* current_object);
bool processSkin(daeDatabase* collada_db, daeElement* collada_document_root, domMesh* current_mesh, domSkin* current_skin, std::unique_ptr<LLLocalMeshObject>& current_object);
void processSkeletonJoint(domNode* current_node, std::map<std::string, std::string>& joint_map, std::map<std::string, LLMatrix4>& joint_transforms);
bool readMesh_CommonElements(const domInputLocalOffset_Array& inputs,
int& offset_position, int& offset_normals, int& offset_uvmap, int& index_stride,
domSource*& source_position, domSource*& source_normals, domSource*& source_uvmap);
std::string getElementName(daeElement* element_current, int fallback_index);
void pushLog(std::string who, std::string what);
// mesh data read functions, basically refactored from what's already in lldaeloader
// consolidating them into a single mega function makes for very sketchy readability.
bool readMesh_Triangle(LLLocalMeshFace* data_out, const domTrianglesRef& data_in);
bool readMesh_Polylist(LLLocalMeshFace* data_out, const domPolylistRef& data_in);
// NOTE: polygon schema
//bool readMesh_Polygons(LLLocalMeshFace* data_out, const domPolygonsRef& data_in);
private:
LLLocalMeshFileLOD mLod;
std::vector<std::string> mLoadingLog;
};