Merge integration of Vaalith's Local_mesh to main
Initial release of local mesh support. Local mesh implementation by Vaalith Jinnmaster
commit
d31a2dacba
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -316,6 +316,8 @@ public:
|
|||
//virtual
|
||||
void updateRiggingInfo();
|
||||
S32 mLastRiggingInfoLOD;
|
||||
bool mIsLocalMesh;
|
||||
bool mIsLocalMeshUsingScale;
|
||||
|
||||
// Functions that deal with media, or media navigation
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue