local mesh beta

meow-7.2.2
Seethe 2022-03-15 08:26:43 +02:00 committed by Beq
parent 01d857ec17
commit c38855897e
14 changed files with 3805 additions and 0 deletions

View File

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

View File

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

View File

@ -0,0 +1,52 @@
#pragma once
#include "llfloater.h"
class LLObjectSelection;
class LLFloaterLocalMesh : public LLFloater
{
public:
LLFloaterLocalMesh(const LLSD& key);
virtual ~LLFloaterLocalMesh(void);
public:
void onOpen(const LLSD& key);
void onClose(bool app_quitting);
void onSelectionChangedCallback();
virtual BOOL postBuild();
virtual void draw();
/* 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 onBtnShowLog(void* userdata);
void reloadFileList(bool keep_selection);
void onFileListCommitCallback();
void reloadLowerUI();
void toggleSelectTool(bool toggle);
LLUUID getCurrentSelectionIfValid();
private:
// 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;
};

View File

@ -0,0 +1,989 @@
// linden headers
#include "llviewerprecompiledheaders.h"
#include "llcallbacklist.h"
#include "llviewerobjectlist.h"
#include "llvovolume.h"
#include "llmeshrepository.h"
#include "llvolumemgr.h"
#include "pipeline.h"
#include "llviewercontrol.h"
// STL headers
#include <chrono>
// boost headers
#include "fix_macros.h"
#include <boost/filesystem.hpp>
// local mesh headers
#include "lllocalmesh.h"
#include "llfloaterlocalmesh.h"
// local mesh importers
#include "lllocalmeshimportdae.h"
/*==========================================*/
/* LLLocalMeshFace: aka submesh denoted by */
/* material assignment. holds per-face - */
/* values for indices, bounding box and */
/* vtx pos, normals, uv coords, weights. */
/*==========================================*/
void LLLocalMeshFace::setFaceBoundingBox(LLVector4 data_in, bool initial_values)
{
if (initial_values)
{
mFaceBoundingBox.first = data_in;
mFaceBoundingBox.second = data_in;
return;
}
for (size_t array_iter = 0; array_iter < 4; ++array_iter)
{
mFaceBoundingBox.first[array_iter] = std::min(mFaceBoundingBox.first[array_iter], data_in[array_iter]);
mFaceBoundingBox.second[array_iter] = std::max(mFaceBoundingBox.second[array_iter], data_in[array_iter]);
}
}
/*==========================================*/
/* LLLocalMeshObject: collection of faces */
/* has object name, transform & skininfo, */
/* volumeid and volumeparams for vobj. */
/* when applied - fills vobj volume. */
/*==========================================*/
LLLocalMeshObject::LLLocalMeshObject(std::string name)
{
mObjectName = name;
mSculptID.generate();
mVolumeParams.setSculptID(mSculptID, LL_SCULPT_TYPE_MESH);
}
LLLocalMeshObject::~LLLocalMeshObject()
{
}
void LLLocalMeshObject::computeObjectBoundingBox()
{
// for the purposes of a bounding box, we only care for LOD3
// unless the user is misusing lower LODs in a weird way.
// we've already checked that mFaces[3] isn't empty in the invoking function.
// check anyway
if (mFaces[3].empty())
{
// why are we still here, just to suffer?
return;
}
auto& lod3_faces = mFaces[3];
// .first is min, .second is max
// set initial values[face 0] to compare against
auto& init_values = lod3_faces[0]->getFaceBoundingBox();
mObjectBoundingBox.first = init_values.first;
mObjectBoundingBox.second = init_values.second;
// process any additional faces
for (size_t face_iter = 1; face_iter < lod3_faces.size(); ++face_iter)
{
auto& current_bbox_min = lod3_faces[face_iter]->getFaceBoundingBox().first;
auto& current_bbox_max = lod3_faces[face_iter]->getFaceBoundingBox().second;
for (size_t array_iter = 0; array_iter < 4; ++array_iter)
{
mObjectBoundingBox.first.mV[array_iter] = std::min(mObjectBoundingBox.first.mV[array_iter], current_bbox_min.mV[array_iter]);
mObjectBoundingBox.second.mV[array_iter] = std::max(mObjectBoundingBox.second.mV[array_iter], current_bbox_max.mV[array_iter]);
}
}
}
void LLLocalMeshObject::computeObjectTransform()
{
// most things here were tactfully stolen from LLModel::normalizeVolumeFaces()
// translation of bounding box to origin
mObjectTranslation = mObjectBoundingBox.first;
mObjectTranslation += mObjectBoundingBox.second;
mObjectTranslation *= -0.5f;
// actual bounding box size
mObjectSize = mObjectBoundingBox.second;
mObjectSize -= mObjectBoundingBox.first;
for (int vec_iter = 0; vec_iter < 4; ++vec_iter)
{
// make sure it can be divided by
if (fabs(mObjectSize.mV[vec_iter]) <= F_APPROXIMATELY_ZERO)
{
mObjectSize.mV[vec_iter] = 1.0f;
}
}
// bounding box scale in a 1Mx3 cube
mObjectScale.set(1.f, 1.f, 1.f);
for (int vec_iter = 0; vec_iter < 4; ++vec_iter)
{
mObjectScale.mV[vec_iter] = mObjectScale.mV[vec_iter] / mObjectSize.mV[vec_iter];
}
}
void LLLocalMeshObject::normalizeFaceValues(LLLocalMeshFileLOD lod_iter)
{
// NOTE: uv extents
// doesn't actually seem necessary, UDIM support "just works".
auto& lod_faces = mFaces[lod_iter];
// if current lod isn't in use, skip.
if (lod_faces.empty())
{
return;
}
// process current lod
for (auto& current_face : lod_faces)
{
// first transform the bounding boxes
auto& current_submesh_bbox = current_face->getFaceBoundingBox();
current_submesh_bbox.first += mObjectTranslation;
current_submesh_bbox.second += mObjectTranslation;
for (int vec_iter = 0; vec_iter < 4; ++vec_iter)
{
current_submesh_bbox.first.mV[vec_iter] *= mObjectScale.mV[vec_iter];
current_submesh_bbox.second.mV[vec_iter] *= mObjectScale.mV[vec_iter];
}
// then transform the positions
auto& current_face_positions = current_face->getPositions();
for (auto& current_position : current_face_positions)
{
current_position += mObjectTranslation;
for (int vec_iter = 0; vec_iter < 4; ++vec_iter)
{
current_position.mV[vec_iter] *= mObjectScale.mV[vec_iter];
}
}
// if we have no normals, or they somehow don't match pos - skip here.
auto& current_face_normals = current_face->getNormals();
if (current_face_normals.size() != current_face_positions.size())
{
continue;
}
// and last, transform the normals
for (auto& current_normal : current_face_normals)
{
if (current_normal.isExactlyZero())
{
continue;
}
for (int vec_iter = 0; vec_iter < 4; ++vec_iter)
{
current_normal.mV[vec_iter] *= mObjectSize.mV[vec_iter];
}
current_normal.normalize();
}
}
}
void LLLocalMeshObject::fillVolume(LLLocalMeshFileLOD lod)
{
// check if we have data for [lod]
if (mFaces[lod].empty())
{
return;
}
// generate face data
std::vector<LLVolumeFace> new_faces;
bool weight_attributes_found = false;
for (auto& current_submesh : mFaces[lod])
{
LLVolumeFace new_face;
new_face.resizeVertices(current_submesh->getPositions().size());
new_face.resizeIndices(current_submesh->getIndices().size());
// NOTE: uv extents
// doesn't actually seem necessary, UDIM support "just works".
// position attribute
for (size_t position_iter = 0; position_iter < current_submesh->getPositions().size(); ++position_iter)
{
new_face.mPositions[position_iter].loadua(current_submesh->getPositions()[position_iter].mV);
}
// normal attribute
for (size_t normal_iter = 0; normal_iter < current_submesh->getNormals().size(); ++normal_iter)
{
new_face.mNormals[normal_iter].loadua(current_submesh->getNormals()[normal_iter].mV);
}
// uv attribute
for (size_t uv_iter = 0; uv_iter < current_submesh->getUVs().size(); ++uv_iter)
{
new_face.mTexCoords[uv_iter] = current_submesh->getUVs()[uv_iter];
}
// weight attribute
if (!current_submesh->getSkin().empty())
{
weight_attributes_found = true;
new_face.allocateWeights(current_submesh->getSkin().size());
for (size_t weight_iter = 0; weight_iter < current_submesh->getSkin().size(); ++weight_iter)
{
auto current_local_weight = current_submesh->getSkin()[weight_iter];
LLVector4 current_v4_weight;
for (int joint_iter = 0; joint_iter < 4; ++joint_iter)
{
auto jidx = current_local_weight.mJointIndices[joint_iter];
if (jidx == -1)
{
continue;
}
float cweight = current_local_weight.mJointWeights[joint_iter];
float cjoint = (float)jidx;
float combined = cweight + cjoint;
current_v4_weight[joint_iter] = combined;
}
new_face.mWeights[weight_iter].loadua(current_v4_weight.mV);
}
}
// transfer indices
for (size_t index_iter = 0; index_iter < current_submesh->getNumIndices(); ++index_iter)
{
new_face.mIndices[index_iter] = current_submesh->getIndices()[index_iter];
}
new_faces.push_back(new_face);
}
// push data into relevant lod
LLVolume* current_volume = LLPrimitive::getVolumeManager()->refVolume(mVolumeParams, lod);
if (current_volume)
{
current_volume->copyFacesFrom(new_faces);
current_volume->setMeshAssetLoaded(TRUE);
LLPrimitive::getVolumeManager()->unrefVolume(current_volume);
}
}
void LLLocalMeshObject::attachSkinInfo()
{
auto skinmap_seeker = gMeshRepo.mSkinMap.find(mSculptID);
if (skinmap_seeker == gMeshRepo.mSkinMap.end())
{
gMeshRepo.mSkinMap[mSculptID] = mMeshSkinInfo;
}
else
{
// NOTE: seems necessary, not tested without.
skinmap_seeker->second = mMeshSkinInfo;
}
}
bool LLLocalMeshObject::getIsRiggedObject()
{
bool result = false;
auto& main_lod_faces = mFaces[LLLocalMeshFileLOD::LOCAL_LOD_HIGH];
// main lod isn't empty
if (!main_lod_faces.empty())
{
auto& skin = main_lod_faces[0]->getSkin();
if (!skin.empty())
{
result = true;
}
}
return result;
}
/*==========================================*/
/* LLLocalMeshFile: Single Unit */
/* owns filenames [main and lods] */
/* owns the loaded local mesh objects */
/*==========================================*/
LLLocalMeshFile::LLLocalMeshFile(std::string filename, bool try_lods)
{
// initialize safe defaults
for (size_t lod_iter = 0; lod_iter < 4; ++lod_iter)
{
mFilenames[lod_iter].empty();
mLastModified[lod_iter].clear();
mLoadedSuccessfully[lod_iter] = false;
}
mTryLODFiles = try_lods;
mShortName.empty();
mLoadingLog.clear();
mExtension = LLLocalMeshFileExtension::EXTEN_NONE;
mLocalMeshFileStatus = LLLocalMeshFileStatus::STATUS_NONE;
mLocalMeshFileID.setNull();
mLocalMeshFileNeedsUIUpdate = false;
mLoadedObjectList.clear();
mSavedObjectSculptIDs.clear();
pushLog("LLLocalMeshFile", "Initializing with filename: " + filename);
// check if main filename exists, just in case
if (!boost::filesystem::exists(filename))
{
// filename provided doesn't exist, just stop.
mLocalMeshFileStatus = LLLocalMeshFileStatus::STATUS_ERROR;
pushLog("LLLocalMeshFile", "Couldn't find filename: " + filename, true);
return;
}
mFilenames[3] = filename;
mShortName = boost::filesystem::path(filename).filename().generic_string();
// check if we have a valid extension, can't switch with string can we?
std::string exten_str = boost::filesystem::extension(filename);
if (exten_str == ".dae")
{
mExtension = LLLocalMeshFileExtension::EXTEN_DAE;
pushLog("LLLocalMeshFile", "Extension found: COLLADA");
}
// add more ifs for different types, in lieu of a better approach?
// if no extensions found, stop.
if (mExtension == LLLocalMeshFileExtension::EXTEN_NONE)
{
mLocalMeshFileStatus = LLLocalMeshFileStatus::STATUS_ERROR;
pushLog("LLLocalMeshFile", "No valid file extension found.", true);
return;
}
mLocalMeshFileID.generate();
// actually loads the files
reloadLocalMeshObjects(true);
}
LLLocalMeshFile::~LLLocalMeshFile()
{
}
void LLLocalMeshFile::reloadLocalMeshObjects(bool initial_load)
{
// if we're already loading - nothing should be calling for a reload,
// but just in case..
if (mLocalMeshFileStatus == LLLocalMeshFileStatus::STATUS_LOADING)
{
return;
}
// if we're reloading, may be a good time to clear the log
if (!initial_load)
{
mLoadingLog.clear();
// before reloading (and regenerating sculpt ids for each object) save these ids
// we'll need them to find the list of vobjects affected by each local object,
// that so we can update vobjects with the new local objects.
// clear it first just in case
mSavedObjectSculptIDs.clear();
for (auto& local_object : mLoadedObjectList)
{
auto id = local_object->getVolumeParams().getSculptID();
mSavedObjectSculptIDs.push_back(id);
}
}
mLocalMeshFileStatus = LLLocalMeshFileStatus::STATUS_LOADING;
mLocalMeshFileNeedsUIUpdate = true;
// another recheck that mFilenames[3] main file is present,
// in case the file got deleted and the user hits reload - it'll error out here.
if (!boost::filesystem::exists(mFilenames[3]))
{
// filename provided doesn't exist, just stop.
mLocalMeshFileStatus = LLLocalMeshFileStatus::STATUS_ERROR;
pushLog("LLLocalMeshFile", "Couldn't find filename: " + mFilenames[3], true);
return;
}
// check for lod filenames
if (mTryLODFiles)
{
size_t dot_position = mFilenames[3].find_last_of(".");
if (dot_position == mFilenames[3].npos)
{
// should theoretically never happen, we did check extension before,
// but "should never happen" doesn't mean we shouldn't check.
pushLog("LLLocalMeshFile", "Filename extension error, loading stopped.", true);
mLocalMeshFileStatus = LLLocalMeshFileStatus::STATUS_ERROR;
return;
}
pushLog("LLLocalMeshFile", "Seeking LOD files...");
// up to LOD2, LOD3 being the highest is always done by this point.
for (size_t lodfile_iter = 0; lodfile_iter < 3; ++lodfile_iter)
{
std::string current_lod_filename = mFilenames[3];
std::string lod_string = "_LOD" + std::to_string(lodfile_iter);
current_lod_filename.insert(dot_position, lod_string);
if (boost::filesystem::exists(current_lod_filename))
{
pushLog("LLLocalMeshFile", "LOD filename " + current_lod_filename + " found, adding.");
mFilenames[lodfile_iter] = current_lod_filename;
}
else
{
pushLog("LLLocalMeshFile", "LOD filename " + current_lod_filename + " not found, skipping.");
mFilenames[lodfile_iter].clear();
}
}
}
else
{
pushLog("LLLocalMeshFile", "Skipping LOD 2-0 files, as specified.");
}
// if we are here we can assume at least mFilenames[3] is present
// here we'll define a lambda to call through std::async, accessible through mAsyncFuture.
// the lamnda returns bool if change happened, individual lod logs, and their status.
auto lambda_loadfiles = [&]() mutable -> LLLocalMeshLoaderReply
{
bool change_happened = false;
std::vector<std::string> log;
std::array<bool, 4> lod_success = {false, false, false, false};
// iterate over every lod
// we're counting back because LOD3 is most likely to have showstopper problems,
// so i'd rather not load other lods if LOD3 is found to be broken.
// int rather than size_t because we're iterating over LODS 3 to 0, stopping at -1
for (signed int lod_idx = 3; lod_idx >= 0; --lod_idx)
{
LLLocalMeshFileLOD current_lod = static_cast<LLLocalMeshFileLOD>(lod_idx);
// do we have a filename for this lod?
if (mFilenames[lod_idx].empty())
{
log.push_back("[ LLLocalMeshFile ] File for LOD " + std::to_string(lod_idx) + " was not found, skipping.");
continue;
}
// has the file been modified since we last checked?
bool file_modified = updateLastModified(current_lod);
if (!file_modified)
{
log.push_back("[ LLLocalMeshFile ] File for LOD " + std::to_string(lod_idx) + " was not modified, skipping.");
lod_success[lod_idx] = mLoadedSuccessfully[lod_idx];
continue;
}
// up until here, skipping loading a lod is fine, after here - it's a sign of an error.
// clear the old objects, if any.
if (lod_idx == LLLocalMeshFileLOD::LOCAL_LOD_HIGH)
{
mLoadedObjectList.clear();
}
log.push_back("[ LLLocalMeshFile ] Attempting to load file for LOD " + std::to_string(lod_idx));
switch (mExtension)
{
case LLLocalMeshFileExtension::EXTEN_DAE:
{
// pass it over to dae loader
LLLocalMeshImportDAE importer;
auto importer_result = importer.loadFile(this, current_lod);
lod_success[lod_idx] = importer_result.first;
// NOTE: if not success - do not indicate change as not to affect existing vobjects?
if (lod_success[lod_idx])
{
change_happened = true;
}
auto importer_log = importer_result.second;
log.reserve(log.size() + importer_log.size());
log.insert(log.end(), importer_log.begin(), importer_log.end());
break;
}
default:
{
log.push_back("[ LLLocalMeshFile ] ERROR, Loader for LOD " + std::to_string(lod_idx) + " called with invalid extension.");
break;
}
}
// by this point, it's only false if there's an actual error loading the file.
if (!lod_success[lod_idx])
{
log.push_back("[ LLLocalMeshFile ] ERROR, attempted and failed to load LOD " + std::to_string(lod_idx) + ", stopping.");
break;
}
}
// just in case, recheck if we actually ended up loading anything
auto& object_list = getObjectVector();
if (object_list.empty())
{
log.push_back("[ LLLocalMeshFile ] ERROR, no objects loaded, stopping.");
// seems like a pretty serious error, error out all the lods manually,
// and set change happened to false.
for (size_t lod_iter = 0; lod_iter < 4; ++lod_iter)
{
lod_success[lod_iter] = false;
}
change_happened = false;
}
LLLocalMeshLoaderReply result;
result.mChanged = change_happened;
result.mLog = log;
result.mStatus = lod_success;
return result;
};
mAsyncFuture = std::async(std::launch::async, lambda_loadfiles);
}
LLLocalMeshFile::LLLocalMeshFileStatus LLLocalMeshFile::reloadLocalMeshObjectsCheck()
{
// only invalid if no loading is happening.
if (mAsyncFuture.valid() == false)
{
return mLocalMeshFileStatus;
}
// actually check if the thread is done, if no - return that we're still loading.
// _Is_ready() doesn't seem to actually do what it promises to, how punny.
// so we'll wait for zero milliseconds instead to check if ready.
auto timeout = std::chrono::milliseconds(0);
if (mAsyncFuture.wait_for(timeout) != std::future_status::ready)
{
// thread is still working
return mLocalMeshFileStatus;
}
else
{
// thread is done working
reloadLocalMeshObjectsCallback();
}
// by now we have either active or error status, this return will lead to ui refresh.
return mLocalMeshFileStatus;
}
void LLLocalMeshFile::reloadLocalMeshObjectsCallback()
{
LLLocalMeshLoaderReply reply = mAsyncFuture.get();
mLoadingLog.reserve(mLoadingLog.size() + reply.mLog.size());
mLoadingLog.insert(mLoadingLog.end(), reply.mLog.begin(), reply.mLog.end());
mLoadedSuccessfully = reply.mStatus;
// if LOD3 is ok, we're technically fine.
if (mLoadedSuccessfully[3])
{
mLocalMeshFileStatus = LLLocalMeshFileStatus::STATUS_ACTIVE;
if (reply.mChanged)
{
updateVObjects();
}
}
else
{
mLocalMeshFileStatus = LLLocalMeshFileStatus::STATUS_ERROR;
}
mLocalMeshFileNeedsUIUpdate = true;
}
bool LLLocalMeshFile::updateLastModified(LLLocalMeshFileLOD lod)
{
bool file_updated = false;
LLSD current_last_modified = mLastModified[lod];
std::string current_filename = mFilenames[lod];
#ifndef LL_WINDOWS
const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(mFilename));
#else
const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(utf8str_to_utf16str(current_filename)));
#endif
LLSD new_last_modified = asctime(localtime(&temp_time));
if (new_last_modified.asString() != current_last_modified.asString())
{
file_updated = true;
mLastModified[lod] = new_last_modified;
}
return file_updated;
}
bool LLLocalMeshFile::notifyNeedsUIUpdate()
{
bool result = mLocalMeshFileNeedsUIUpdate;
if (mLocalMeshFileNeedsUIUpdate)
{
// ui update can only be needed once.
mLocalMeshFileNeedsUIUpdate = false;
}
return result;
}
LLLocalMeshFile::LLLocalMeshFileInfo LLLocalMeshFile::getFileInfo()
{
LLLocalMeshFileInfo result;
result.mName = mShortName;
result.mStatus = mLocalMeshFileStatus;
result.mLocalID = mLocalMeshFileID;
result.mLODAvailability = mLoadedSuccessfully;
result.mObjectList.clear();
if (mLocalMeshFileStatus == LLLocalMeshFileStatus::STATUS_ACTIVE)
{
// fill object list
auto& vector_objects = getObjectVector();
for (auto& current_object : vector_objects)
{
std::string object_name = current_object->getObjectName();
result.mObjectList.push_back(object_name);
}
}
return result;
}
void LLLocalMeshFile::updateVObjects()
{
for (size_t object_iter = 0; object_iter < mSavedObjectSculptIDs.size(); ++object_iter)
{
auto local_obj_sculpt_id = mSavedObjectSculptIDs[object_iter];
auto affected_vobject_ids = gObjectList.findMeshObjectsBySculptID(local_obj_sculpt_id);
for (auto current_vobject_id : affected_vobject_ids)
{
auto target_object_ptr = static_cast<LLVOVolume*>(gObjectList.findObject(current_vobject_id));
if (!target_object_ptr->mIsLocalMesh)
{
// in case for some reason we got the id of an unrelated object?
// should never get here.
continue;
}
bool using_scale = target_object_ptr->mIsLocalMeshUsingScale;
applyToVObject(current_vobject_id, object_iter, using_scale);
}
// also, remove old skin from
gMeshRepo.mSkinMap.erase(local_obj_sculpt_id);
}
}
void LLLocalMeshFile::applyToVObject(LLUUID viewer_object_id, int object_index, bool use_scale)
{
// get the actual vovolume
auto target_object = static_cast<LLVOVolume*>(gObjectList.findObject(viewer_object_id));
if (target_object == nullptr)
{
return;
}
// check if object_index is sane
if ((object_index < 0) || (object_index >= mLoadedObjectList.size()))
{
return;
}
auto object_params = mLoadedObjectList[object_index]->getVolumeParams();
target_object->setVolume(object_params, 3);
target_object->mIsLocalMesh = true;
target_object->mIsLocalMeshUsingScale = use_scale;
for (int lod_reverse_iter = 3; lod_reverse_iter >= 0; --lod_reverse_iter)
{
if (!mLoadedSuccessfully[lod_reverse_iter])
{
continue;
}
LLLocalMeshFileLOD current_lod = static_cast<LLLocalMeshFileLOD>(lod_reverse_iter);
mLoadedObjectList[object_index]->fillVolume(current_lod);
}
if (mLoadedObjectList[object_index]->getIsRiggedObject())
{
mLoadedObjectList[object_index]->attachSkinInfo();
}
if ((!target_object->isAttachment()) && use_scale)
{
auto scale = mLoadedObjectList[object_index]->getObjectSize();
scale *= 0.01; /* NOTE: magic number */
target_object->setScale(LLVector3(scale), false);
}
// force refresh (selected/edit mode won't let it redraw otherwise)
gPipeline.markRebuild(target_object->mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
// NOTE: this ^^ (or lod change) causes renderer crash on mesh with degenerate primitives.
}
void LLLocalMeshFile::pushLog(std::string who, std::string what, bool is_error)
{
std::string log_msg = "[ " + who + " ] ";
if (is_error)
{
log_msg += "[ ERROR ] ";
}
log_msg += what;
mLoadingLog.push_back(log_msg);
}
/*==========================================*/
/* LLLocalMeshSystem: Main Manager Class */
/* user facing manager class */
/*==========================================*/
LLLocalMeshSystem::LLLocalMeshSystem()
{
mLoadedFileList.clear();
mFileAsyncsOngoing = false;
mFloaterPtr = nullptr;
}
LLLocalMeshSystem::~LLLocalMeshSystem()
{
/* clear files, triggers releasing donor objects
and destructs any held localmesh objects. */
mLoadedFileList.clear();
}
void LLLocalMeshSystem::addFile(std::string filename, bool try_lods)
{
auto loaded_file = std::make_unique<LLLocalMeshFile>(filename, try_lods);
mLoadedFileList.push_back(std::move(loaded_file));
triggerFloaterRefresh();
triggerCheckFileAsyncStatus();
}
void LLLocalMeshSystem::deleteFile(LLUUID local_file_id)
{
bool delete_done = false;
for(auto iterator = mLoadedFileList.begin(); iterator != mLoadedFileList.end();)
{
auto current_file = iterator->get();
auto current_info = current_file->getFileInfo();
if (current_info.mLocalID == local_file_id)
{
// check if the file is currently in a state of loading
// even though the ui button will be disabled while loading -
// this check still feels necessary.
if (current_info.mStatus == LLLocalMeshFile::LLLocalMeshFileStatus::STATUS_LOADING)
{
break;
}
// found the requested by id file, it's not in the process of loading, time to delete it.
iterator = mLoadedFileList.erase(std::move(iterator));
delete_done = true;
}
else
{
++iterator;
}
}
if (delete_done)
{
triggerFloaterRefresh();
}
}
void LLLocalMeshSystem::reloadFile(LLUUID local_file_id)
{
bool reload_started = false;
for (auto iterator = mLoadedFileList.begin(); iterator != mLoadedFileList.end(); ++iterator)
{
auto current_file = iterator->get();
auto current_info = current_file->getFileInfo();
if (current_info.mLocalID == local_file_id)
{
// check if the file is currently in a state of loading
// same deal as above, ui button will be disabled but checking regardless.
if (current_info.mStatus == LLLocalMeshFile::LLLocalMeshFileStatus::STATUS_LOADING)
{
continue;
}
// found the requested by id file, it's not loading, let's make it loading.
current_file->reloadLocalMeshObjects();
reload_started = true;
}
}
if (reload_started)
{
triggerCheckFileAsyncStatus();
}
}
void LLLocalMeshSystem::applyVObject(LLUUID viewer_object_id, LLUUID local_file_id, int object_index, bool use_scale)
{
for (auto& loaded_file : mLoadedFileList)
{
if (loaded_file->getFileID() == local_file_id)
{
loaded_file->applyToVObject(viewer_object_id, object_index, use_scale);
break;
}
}
}
void LLLocalMeshSystem::clearVObject(LLUUID viewer_object_id)
{
auto target_object = (LLVOVolume*)gObjectList.findObject(viewer_object_id);
if (!target_object)
{
return;
}
target_object->mIsLocalMesh = false;
// get original params
auto sculpt_params = (LLSculptParams*)target_object->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
auto sculpt_id = sculpt_params->getSculptTexture();
LLVolumeParams volume_params;
volume_params.setSculptID(sculpt_id, LL_SCULPT_TYPE_MESH);
target_object->setVolume(volume_params, 3);
}
void LLLocalMeshSystem::triggerCheckFileAsyncStatus()
{
// async already ongoing, just wait patiently.
if (mFileAsyncsOngoing)
{
return;
}
// this is here so it's set off /manually/ rather than by a function called through doOnIdleOneTime
mFileAsyncsOngoing = true;
checkFileAsyncStatus();
}
void LLLocalMeshSystem::checkFileAsyncStatus()
{
// in case of misuse.
// use triggerCheckFileAsyncStatus to trigger this instead.
// the reason is: this function calls itself through doOnIdleOneTime,
// so triggerCheckFileAsyncStatus is used this function doesn't set mFileAsyncsOngoing to true
if (!mFileAsyncsOngoing)
{
return;
}
bool found_active_asyncs = false;
bool need_ui_update = false;
for (auto& current_file : mLoadedFileList)
{
// reloadLocalMeshObjectsCheck actually pings the future to check if ready,
// and if yes - sets proper status, so we're pinging all files.
// and if the file isn't loading - it'll just safely return.
auto file_status = current_file->reloadLocalMeshObjectsCheck();
if (file_status == LLLocalMeshFile::LLLocalMeshFileStatus::STATUS_LOADING)
{
found_active_asyncs = true;
}
if (current_file->notifyNeedsUIUpdate())
{
need_ui_update = true;
}
}
if (found_active_asyncs)
{
// come back here next tick to ping checkFileAsyncStatus again
doOnIdleOneTime(boost::bind(&LLLocalMeshSystem::checkFileAsyncStatus, this));
}
else
{
// nothing is updating, reflect that in mFileAsyncsOngoing
mFileAsyncsOngoing = false;
}
if (need_ui_update)
{
triggerFloaterRefresh();
}
}
void LLLocalMeshSystem::registerFloaterPointer(LLFloaterLocalMesh* floater_ptr)
{
// not checking for nullptr here, we use this to both register and de-register a floater,
// so nullptr is legal here.
mFloaterPtr = floater_ptr;
}
void LLLocalMeshSystem::triggerFloaterRefresh()
{
if (mFloaterPtr)
{
mFloaterPtr->reloadFileList(true);
}
}
std::vector<LLLocalMeshFile::LLLocalMeshFileInfo> LLLocalMeshSystem::getFileInfoVector()
{
std::vector<LLLocalMeshFile::LLLocalMeshFileInfo> result;
for (auto& current_file : mLoadedFileList)
{
result.push_back(current_file->getFileInfo());
}
return result;
}
std::vector<std::string> LLLocalMeshSystem::getFileLog(LLUUID local_file_id)
{
std::vector<std::string> result;
for (auto& current_file : mLoadedFileList)
{
if (current_file->getFileID() == local_file_id)
{
result = current_file->getFileLog();
break;
}
}
return result;
}

234
indra/newview/lllocalmesh.h Normal file
View File

@ -0,0 +1,234 @@
#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
};
/*==========================================*/
/* 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;
};
public:
void setFaceBoundingBox(LLVector4 data_in, bool initial_values = false);
int getNumVerts() { return mPositions.size(); }
int getNumIndices() { 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
LLLocalMeshObject(std::string name);
~LLLocalMeshObject();
public:
// translation and scale
void computeObjectBoundingBox();
void computeObjectTransform();
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; };
std::string getObjectName() { return mObjectName; };
LLVector4 getObjectTranslation() { return mObjectTranslation; };
LLVector4 getObjectSize() { return mObjectSize; };
LLVector4 getObjectScale() { return mObjectScale; };
LLMeshSkinInfo& getObjectMeshSkinInfo() { return mMeshSkinInfo; };
LLVolumeParams getVolumeParams() { return mVolumeParams; };
bool getIsRiggedObject();
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 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;
};
public:
// life cycle management
LLLocalMeshFile(std::string filename, bool try_lods);
~LLLocalMeshFile();
// disallowing copy
LLLocalMeshFile(const LLLocalMeshFile& local_mesh_file) = delete;
LLLocalMeshFile& operator=(const LLLocalMeshFile& local_mesh_file) = delete;
public:
// 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) { return mFilenames[lod]; };
LLUUID getFileID() { return mLocalMeshFileID; };
std::vector<std::string> getFileLog() { return mLoadingLog; };
// viewer object
void updateVObjects();
void applyToVObject(LLUUID viewer_object_id, int object_index, bool use_scale);
// misc
void pushLog(std::string who, std::string what, bool is_error = false);
private:
std::array<std::string, 4> mFilenames;
std::array<LLSD, 4> mLastModified;
std::array<bool, 4> 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>
{
public:
// life cycle management
LLSINGLETON(LLLocalMeshSystem);
~LLLocalMeshSystem();
public:
// file management
void addFile(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();
std::vector<LLLocalMeshFile::LLLocalMeshFileInfo> getFileInfoVector();
std::vector<std::string> getFileLog(LLUUID local_file_id);
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,96 @@
/**
* @file lllocalmeshimportdae.h
* @author Vaalith Jinn
* @brief Local Mesh Import DAE header
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#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;
};

View File

@ -221,6 +221,9 @@
#include "NACLfloaterexploresounds.h"
#include "particleeditor.h"
#include "quickprefs.h"
#include "llfloaterlocalmesh.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", "llfloaterlocalmesh.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 TEMP TODO
// local mesh begin // TODO: check if necessary here, or can be granular.
if (mIsLocalMesh == true)
{
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;
@ -4557,6 +4567,7 @@ U32 LLVOVolume::getHighLODTriangleCount()
U32 LLVOVolume::getLODTriangleCount(S32 lod)
{
U32 ret = 0;
return ret; // LOCAL MESH TEMP TODO
LLVolume* volume = getVolume();

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,145 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
positioning="centered"
legacy_header_height="18"
can_resize="true"
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">
<scroll_list
name="l_name_list"
left="10"
top="25"
width="485"
height="223"
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" />
</scroll_list>
<text_editor
name="text_log"
max_length="65536"
left="10"
top="25"
width="485"
height="223"
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="false"
visible="false">
</text_editor>
<button
height="18"
label="Apply"
layout="topleft"
left="10"
top="253"
name="btn_apply"
width="75"
visible="true"/>
<check_box
height="18"
label="Apply Scale"
layout="topleft"
left="90"
top="253"
name="chkbox_use_scale"
width="75"
visible="true"
follows="bottom"/>
<combo_box
height="18"
layout="topleft"
follows="left|top|bottom"
name="object_apply_list"
left="180"
top="253"
width="315"
visible="true">
/>
</combo_box>
<button
height="18"
label="Add"
layout="topleft"
left="10"
top="275"
name="btn_add"
width="75"
visible="true"/>
<check_box
height="18"
label="Use LODs"
layout="topleft"
left="90"
top="275"
name="chkbox_use_lods"
width="75"
visible="true"
follows="bottom"/>
<button
height="18"
label="Remove"
layout="topleft"
left="180"
top="275"
name="btn_remove"
width="75"
visible="true"/>
<button
height="18"
label="Reload"
layout="topleft"
left="260"
top="275"
name="btn_reload"
width="75"
visible="true"/>
<button
height="18"
label="Clear"
layout="topleft"
left="340"
top="275"
name="btn_clear"
width="75"
visible="true"/>
<button
height="18"
label="Show Log"
layout="topleft"
left="420"
top="275"
name="btn_showlog"
width="75"
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"