1114 lines
26 KiB
C++
1114 lines
26 KiB
C++
/**
|
|
* @file llfloaterimportcollada.cpp
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2001-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llfloaterimportcollada.h"
|
|
|
|
#include "dae.h"
|
|
//#include "dom.h"
|
|
#include "dom/domAsset.h"
|
|
#include "dom/domBind_material.h"
|
|
#include "dom/domConstants.h"
|
|
#include "dom/domEffect.h"
|
|
#include "dom/domGeometry.h"
|
|
#include "dom/domInstance_geometry.h"
|
|
#include "dom/domInstance_material.h"
|
|
#include "dom/domInstance_node.h"
|
|
#include "dom/domInstance_effect.h"
|
|
#include "dom/domMaterial.h"
|
|
#include "dom/domMatrix.h"
|
|
#include "dom/domNode.h"
|
|
#include "dom/domProfile_COMMON.h"
|
|
#include "dom/domRotate.h"
|
|
#include "dom/domScale.h"
|
|
#include "dom/domTranslate.h"
|
|
#include "dom/domVisual_scene.h"
|
|
|
|
#include "llagent.h"
|
|
#include "llassetuploadresponders.h"
|
|
#include "lleconomy.h"
|
|
#include "llfloaterperms.h"
|
|
#include "llfloaterreg.h"
|
|
#include "llsdutil.h"
|
|
#include "llsdutil_math.h"
|
|
#include "llselectmgr.h"
|
|
#include "llvfile.h"
|
|
#include "llvfs.h"
|
|
#include "llviewermenufile.h"
|
|
#include "llviewerregion.h"
|
|
#include "llvolumemessage.h"
|
|
#include "llmodel.h"
|
|
#include "llmeshreduction.h"
|
|
#include "material_codes.h"
|
|
|
|
//
|
|
// floater
|
|
//
|
|
|
|
LLFloaterImportCollada::LLFloaterImportCollada(const LLSD& key)
|
|
: LLFloater(key)
|
|
{
|
|
}
|
|
|
|
|
|
BOOL LLFloaterImportCollada::postBuild()
|
|
{
|
|
if (!LLFloater::postBuild())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
childSetCommitCallback("ok", LLImportCollada::onCommitOK, this);
|
|
childSetCommitCallback("cancel", LLImportCollada::onCommitCancel, this);
|
|
|
|
setStatusIdle();
|
|
setAssetCount(0,0);
|
|
enableOK(TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void LLFloaterImportCollada::setAssetCount(S32 mesh_count, S32 texture_count)
|
|
{
|
|
childSetTextArg("mesh count", "[COUNT]", llformat("%d", mesh_count));
|
|
childSetTextArg("texture count", "[COUNT]", llformat("%d", texture_count));
|
|
}
|
|
|
|
void LLFloaterImportCollada::setStatusAssetUploading(std::string asset_name)
|
|
{
|
|
LLUIString uploading = getString("status_uploading");
|
|
uploading.setArg("[NAME]", asset_name);
|
|
childSetTextArg("status", "[STATUS]", uploading.getString());
|
|
}
|
|
|
|
void LLFloaterImportCollada::setStatusCreatingPrim(std::string prim_name)
|
|
{
|
|
LLUIString creating = getString("status_creating");
|
|
creating.setArg("[NAME]", prim_name);
|
|
childSetTextArg("status", "[STATUS]", creating.getString());
|
|
}
|
|
|
|
void LLFloaterImportCollada::setStatusIdle()
|
|
{
|
|
childSetTextArg("status", "[STATUS]", getString("status_idle"));
|
|
}
|
|
|
|
void LLFloaterImportCollada::enableOK(BOOL enable)
|
|
{
|
|
childSetEnabled("ok", enable);
|
|
}
|
|
|
|
|
|
//
|
|
// misc helpers
|
|
//
|
|
|
|
|
|
// why oh why do forbid matrix multiplication in our llmath library?
|
|
LLMatrix4 matrix_multiply(LLMatrix4 a, LLMatrix4 b)
|
|
{
|
|
a *= b;
|
|
|
|
return a;
|
|
}
|
|
|
|
// why oh why does colladadom not provide such things?
|
|
daeElement* getFirstChild(daeElement* parent)
|
|
{
|
|
daeTArray< daeSmartRef<daeElement> > children = parent->getChildren();
|
|
|
|
if (children.getCount() > 0)
|
|
{
|
|
return children[0];
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// why oh why does colladadom not provide such things?
|
|
daeElement* getNextSibling(daeElement* child)
|
|
{
|
|
daeElement* parent = child->getParent();
|
|
|
|
if (parent == NULL)
|
|
{
|
|
// must be root, root has no siblings
|
|
return NULL;
|
|
}
|
|
|
|
daeElement* sibling = NULL;
|
|
|
|
daeTArray< daeSmartRef<daeElement> > children = parent->getChildren();
|
|
for (S32 i = 0; i < children.getCount(); i++)
|
|
{
|
|
if (child == children[i])
|
|
{
|
|
if ((i+1) < children.getCount())
|
|
{
|
|
sibling = children[i+1];
|
|
}
|
|
}
|
|
}
|
|
|
|
return sibling;
|
|
}
|
|
|
|
// try to get a decent label for this element
|
|
std::string getElementLabel(daeElement *element)
|
|
{
|
|
// if we have a name attribute, use it
|
|
std::string name = element->getAttribute("name");
|
|
if (name.length())
|
|
{
|
|
return name;
|
|
}
|
|
|
|
// if we have an ID attribute, use it
|
|
if (element->getID())
|
|
{
|
|
return std::string(element->getID());
|
|
}
|
|
|
|
// if we have a parent, use it
|
|
daeElement* parent = element->getParent();
|
|
if (parent)
|
|
{
|
|
// if parent has a name, use it
|
|
std::string name = parent->getAttribute("name");
|
|
if (name.length())
|
|
{
|
|
return name;
|
|
}
|
|
|
|
// if parent has an ID, use it
|
|
if (parent->getID())
|
|
{
|
|
return std::string(parent->getID());
|
|
}
|
|
}
|
|
|
|
// try to use our type
|
|
daeString element_name = element->getElementName();
|
|
if (element_name)
|
|
{
|
|
return std::string(element_name);
|
|
}
|
|
|
|
// if all else fails, use "object"
|
|
return std::string("object");
|
|
}
|
|
|
|
LLColor4 getDaeColor(daeElement* element)
|
|
{
|
|
LLColor4 value;
|
|
domCommon_color_or_texture_type_complexType::domColor* color =
|
|
daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color"));
|
|
if (color)
|
|
{
|
|
domFx_color_common domfx_color = color->getValue();
|
|
value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
LLTextureEntry profileToTextureEntry(domProfile_COMMON* material)
|
|
{
|
|
LLTextureEntry te;
|
|
|
|
te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture
|
|
daeElement* diffuse = material->getDescendant("diffuse");
|
|
if (diffuse)
|
|
{
|
|
te.setColor(LLColor3(0.1f, 0.9f, 1.0f));
|
|
|
|
domCommon_color_or_texture_type_complexType::domTexture* texture =
|
|
daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture"));
|
|
if (texture)
|
|
{
|
|
domCommon_newparam_type_Array newparams = material->getNewparam_array();
|
|
for (S32 i = 0; i < newparams.getCount(); i++)
|
|
{
|
|
domFx_surface_common* surface = newparams[i]->getSurface();
|
|
if (surface)
|
|
{
|
|
domFx_surface_init_common* init = surface->getFx_surface_init_common();
|
|
if (init)
|
|
{
|
|
domFx_surface_init_from_common_Array init_from = init->getInit_from_array();
|
|
|
|
if (init_from.getCount() > 0)
|
|
{
|
|
daeElement* image = init_from[0]->getValue().getElement();
|
|
if (image)
|
|
{
|
|
LLUUID texture_asset = LLImportColladaAssetCache::getInstance()->getAssetForDaeElement(image);
|
|
|
|
if (texture_asset.notNull())
|
|
{
|
|
te.setID(texture_asset);
|
|
te.setColor(LLColor3(1,1,1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
domCommon_color_or_texture_type_complexType::domColor* color =
|
|
daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color"));
|
|
if (color)
|
|
{
|
|
domFx_color_common domfx_color = color->getValue();
|
|
LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]);
|
|
te.setColor(value);
|
|
}
|
|
}
|
|
|
|
daeElement* emission = material->getDescendant("emission");
|
|
if (emission)
|
|
{
|
|
LLColor4 emission_color = getDaeColor(emission);
|
|
if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25)
|
|
{
|
|
te.setFullbright(TRUE);
|
|
}
|
|
}
|
|
|
|
return te;
|
|
}
|
|
|
|
std::vector<LLTextureEntry> getMaterials(LLModel* model, domInstance_geometry* instance_geo)
|
|
{
|
|
std::vector<LLTextureEntry> texture_entries;
|
|
for (int i = 0; i < model->mMaterialList.size(); i++)
|
|
{
|
|
LLTextureEntry texture_entry;
|
|
|
|
domInstance_material* instance_mat = NULL;
|
|
|
|
domBind_material::domTechnique_common* technique =
|
|
daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID())));
|
|
|
|
if (technique)
|
|
{
|
|
daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>();
|
|
for (int j = 0; j < inst_materials.getCount(); j++)
|
|
{
|
|
std::string symbol(inst_materials[j]->getSymbol());
|
|
|
|
if (symbol == model->mMaterialList[i]) // found the binding
|
|
{
|
|
instance_mat = inst_materials[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (instance_mat)
|
|
{
|
|
domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement());
|
|
if (material)
|
|
{
|
|
domInstance_effect* instance_effect =
|
|
daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID())));
|
|
if (instance_effect)
|
|
{
|
|
domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement());
|
|
if (effect)
|
|
{
|
|
domProfile_COMMON* profile =
|
|
daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID())));
|
|
if (profile)
|
|
{
|
|
texture_entry = profileToTextureEntry(profile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
texture_entries.push_back(texture_entry);
|
|
}
|
|
|
|
return texture_entries;
|
|
}
|
|
|
|
LLTextureEntry instanceGeoToTextureEntry(domInstance_geometry* instance_geo)
|
|
{
|
|
LLTextureEntry te;
|
|
domInstance_material* instance_mat =
|
|
daeSafeCast<domInstance_material>(instance_geo->getDescendant(daeElement::matchType(domInstance_material::ID())));
|
|
if (instance_mat)
|
|
{
|
|
}
|
|
|
|
return te;
|
|
}
|
|
|
|
|
|
|
|
// responder for asset uploads
|
|
// does all the normal stuff followed by a notification to continue importing
|
|
// WARNING - currently unused - TODO
|
|
class LLColladaNewAgentInventoryResponder : public LLNewAgentInventoryResponder
|
|
{
|
|
LLColladaNewAgentInventoryResponder(const LLSD& post_data,
|
|
const LLUUID& vfile_id,
|
|
LLAssetType::EType asset_type)
|
|
: LLNewAgentInventoryResponder(post_data, vfile_id, asset_type)
|
|
{
|
|
}
|
|
LLColladaNewAgentInventoryResponder(const LLSD& post_data,
|
|
const std::string& file_name,
|
|
LLAssetType::EType asset_type)
|
|
: LLNewAgentInventoryResponder(post_data, file_name, asset_type)
|
|
{
|
|
}
|
|
|
|
virtual void uploadComplete(const LLSD& content)
|
|
{
|
|
LLNewAgentInventoryResponder::uploadComplete(content);
|
|
}
|
|
|
|
};
|
|
|
|
BOOL LLImportColladaAssetCache::uploadImageAsset(domImage* image)
|
|
{
|
|
// we only support init_from now - embedded data will come later
|
|
domImage::domInit_from* init = image->getInit_from();
|
|
if (!init)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
std::string filename = cdom::uriToNativePath(init->getValue().str());
|
|
|
|
std::string name = getElementLabel(image);
|
|
|
|
LLUUID transaction_id = upload_new_resource(filename, name, std::string(),
|
|
0, LLFolderType::FT_TEXTURE, LLInventoryType::IT_TEXTURE,
|
|
LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(),
|
|
LLFloaterPerms::getEveryonePerms(),
|
|
name, NULL,
|
|
LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(), NULL);
|
|
|
|
if (transaction_id.isNull())
|
|
{
|
|
llwarns << "cannot upload " << filename << llendl;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
mTransactionMap[transaction_id] = image;
|
|
|
|
LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->setStatusAssetUploading(name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// asset cache -
|
|
// uploads assets and provides a map from collada element to asset
|
|
//
|
|
|
|
|
|
|
|
BOOL LLImportColladaAssetCache::uploadMeshAsset(domMesh* mesh)
|
|
{
|
|
LLPointer<LLModel> model = LLModel::loadModelFromDomMesh(mesh);
|
|
|
|
if (model->getNumVolumeFaces() == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// generate LODs
|
|
|
|
|
|
std::vector<LLPointer<LLModel> > lods;
|
|
lods.push_back(model);
|
|
S32 triangle_count = model->getNumTriangles();
|
|
|
|
for (S32 i = 0; i < 4; i++)
|
|
{
|
|
LLPointer<LLModel> last_model = lods.back();
|
|
|
|
S32 triangle_target = (S32)(triangle_count / pow(3.f, i + 1));
|
|
if (triangle_target > 16)
|
|
{
|
|
LLMeshReduction reduction;
|
|
LLPointer<LLModel> new_model = reduction.reduce(model, triangle_target, LLMeshReduction::TRIANGLE_BUDGET);
|
|
lods.push_back(new_model);
|
|
}
|
|
else
|
|
{
|
|
lods.push_back(last_model);
|
|
}
|
|
}
|
|
|
|
// write model to temp file
|
|
|
|
std::string filename = gDirUtilp->getTempFilename();
|
|
LLModel::writeModel(filename,
|
|
lods[4],
|
|
lods[0],
|
|
lods[1],
|
|
lods[2],
|
|
lods[3],
|
|
lods[4]->mPhysicsShape);
|
|
|
|
|
|
// copy file to VFS
|
|
|
|
LLTransactionID tid;
|
|
tid.generate();
|
|
LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID()); // create asset uuid
|
|
|
|
S32 file_size;
|
|
LLAPRFile infile ;
|
|
infile.open(filename, LL_APR_RB, NULL, &file_size);
|
|
|
|
if (infile.getFileHandle())
|
|
{
|
|
LLVFile file(gVFS, uuid, LLAssetType::AT_MESH, LLVFile::WRITE);
|
|
|
|
file.setMaxSize(file_size);
|
|
|
|
const S32 buf_size = 65536;
|
|
U8 copy_buf[buf_size];
|
|
while ((file_size = infile.read(copy_buf, buf_size)))
|
|
{
|
|
file.write(copy_buf, file_size);
|
|
}
|
|
}
|
|
|
|
|
|
std::string name = getElementLabel(mesh);
|
|
|
|
upload_new_resource(tid, LLAssetType::AT_MESH, name, std::string(), 0,LLFolderType::FT_MESH, LLInventoryType::IT_MESH,
|
|
LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(),
|
|
name, NULL,
|
|
LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(), NULL);
|
|
|
|
LLFile::remove(filename);
|
|
|
|
mTransactionMap[uuid] = mesh;
|
|
|
|
LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->setStatusAssetUploading(name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// called by the mesh asset upload responder to indicate the mesh asset has been uploaded
|
|
void LLImportColladaAssetCache::assetUploaded(LLUUID transaction_uuid, LLUUID asset_uuid, BOOL success)
|
|
{
|
|
std::map<LLUUID, daeElement*>::iterator i = mTransactionMap.find(transaction_uuid);
|
|
|
|
if (i != mTransactionMap.end())
|
|
{
|
|
daeElement* element = i->second;
|
|
|
|
|
|
if (success)
|
|
{
|
|
mAssetMap[element] = asset_uuid;
|
|
}
|
|
else // failure
|
|
{
|
|
// if failed, put back on end of queue
|
|
mUploadsPending.push_back(element);
|
|
}
|
|
|
|
mUploads--;
|
|
uploadNextAsset();
|
|
}
|
|
}
|
|
|
|
const S32 MAX_CONCURRENT_UPLOADS = 5;
|
|
|
|
void LLImportColladaAssetCache::uploadNextAsset()
|
|
{
|
|
while ((mUploadsPending.size() > 0) && (mUploads < MAX_CONCURRENT_UPLOADS))
|
|
{
|
|
BOOL upload_started = FALSE;
|
|
|
|
daeElement* element = mUploadsPending.back();
|
|
mUploadsPending.pop_back();
|
|
|
|
domImage* image = daeSafeCast<domImage>(element);
|
|
if (image)
|
|
{
|
|
upload_started = uploadImageAsset(image);
|
|
}
|
|
|
|
domMesh* mesh = daeSafeCast<domMesh>(element);
|
|
if (mesh)
|
|
{
|
|
upload_started = uploadMeshAsset(mesh);
|
|
}
|
|
|
|
if (upload_started)
|
|
{
|
|
mUploads++;
|
|
}
|
|
|
|
}
|
|
|
|
if ((mUploadsPending.size() == 0) && (mUploads == 0))
|
|
{
|
|
// we're done! notify the importer
|
|
LLImportCollada::getInstance()->assetsUploaded();
|
|
}
|
|
|
|
updateCount();
|
|
}
|
|
|
|
|
|
void LLImportColladaAssetCache::clear()
|
|
{
|
|
mDAE = NULL;
|
|
mTransactionMap.clear();
|
|
mAssetMap.clear();
|
|
mUploadsPending.clear();
|
|
mUploads = 0;
|
|
}
|
|
|
|
void LLImportColladaAssetCache::endImport()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void LLImportColladaAssetCache::updateCount()
|
|
{
|
|
S32 mesh_count = 0;
|
|
S32 image_count = 0;
|
|
|
|
for (S32 i = 0; i < mUploadsPending.size(); i++)
|
|
{
|
|
daeElement* element = mUploadsPending[i];
|
|
|
|
if (daeSafeCast<domMesh>(element))
|
|
{
|
|
mesh_count++;
|
|
}
|
|
|
|
if (daeSafeCast<domImage>(element))
|
|
{
|
|
image_count++;
|
|
}
|
|
}
|
|
|
|
LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->setAssetCount(mesh_count, image_count);
|
|
}
|
|
|
|
void LLImportColladaAssetCache::prepareForUpload(DAE* dae)
|
|
{
|
|
clear();
|
|
mDAE = dae;
|
|
|
|
daeDatabase* db = mDAE->getDatabase();
|
|
|
|
S32 mesh_count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
|
|
for (S32 i = 0; i < mesh_count; i++)
|
|
{
|
|
domMesh* mesh = NULL;
|
|
|
|
db->getElement((daeElement**) &mesh, i, NULL, COLLADA_TYPE_MESH);
|
|
|
|
mUploadsPending.push_back(mesh);
|
|
}
|
|
|
|
|
|
S32 image_count = db->getElementCount(NULL, COLLADA_TYPE_IMAGE);
|
|
for (S32 i = 0; i < image_count; i++)
|
|
{
|
|
domImage* image = NULL;
|
|
db->getElement((daeElement**) &image, i, NULL, COLLADA_TYPE_IMAGE);
|
|
|
|
mUploadsPending.push_back(image);
|
|
}
|
|
|
|
updateCount();
|
|
}
|
|
|
|
|
|
void LLImportColladaAssetCache::uploadAssets()
|
|
{
|
|
uploadNextAsset();
|
|
}
|
|
|
|
|
|
LLUUID LLImportColladaAssetCache::getAssetForDaeElement(daeElement* element)
|
|
{
|
|
LLUUID id;
|
|
|
|
std::map<daeElement*, LLUUID>::iterator i = mAssetMap.find(element);
|
|
if (i != mAssetMap.end())
|
|
{
|
|
id = i->second;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
//
|
|
// importer
|
|
//
|
|
|
|
|
|
|
|
LLImportCollada::LLImportCollada()
|
|
{
|
|
mIsImporting = FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
void LLImportCollada::appendObjectAsset(domInstance_geometry* instance_geo)
|
|
{
|
|
domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement());
|
|
if (!geo)
|
|
{
|
|
llwarns << "cannot find geometry" << llendl;
|
|
return;
|
|
}
|
|
|
|
domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID())));
|
|
if (!mesh)
|
|
{
|
|
llwarns << "could not find mesh" << llendl;
|
|
return;
|
|
}
|
|
|
|
LLUUID mesh_asset = LLImportColladaAssetCache::getInstance()->getAssetForDaeElement(mesh);
|
|
if (mesh_asset.isNull())
|
|
{
|
|
llwarns << "no mesh asset, skipping" << llendl;
|
|
return;
|
|
}
|
|
|
|
// load the model
|
|
LLModel* model = LLModel::loadModelFromDomMesh(mesh);
|
|
|
|
|
|
|
|
|
|
// get our local transformation
|
|
LLMatrix4 transformation = mStack.front().transformation;
|
|
|
|
// adjust the transformation to compensate for mesh normalization
|
|
LLVector3 mesh_scale_vector;
|
|
LLVector3 mesh_translation_vector;
|
|
model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
|
|
LLMatrix4 mesh_translation;
|
|
mesh_translation.setTranslation(mesh_translation_vector);
|
|
transformation = matrix_multiply(mesh_translation, transformation);
|
|
LLMatrix4 mesh_scale;
|
|
mesh_scale.initScale(mesh_scale_vector);
|
|
transformation = matrix_multiply(mesh_scale, transformation);
|
|
|
|
// check for reflection
|
|
BOOL reflected = (transformation.determinant() < 0);
|
|
|
|
// compute position
|
|
LLVector3 position = LLVector3(0, 0, 0) * transformation;
|
|
|
|
// compute scale
|
|
LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
|
|
LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
|
|
LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
|
|
F32 x_length = x_transformed.normalize();
|
|
F32 y_length = y_transformed.normalize();
|
|
F32 z_length = z_transformed.normalize();
|
|
LLVector3 scale = LLVector3(x_length, y_length, z_length);
|
|
|
|
// adjust for "reflected" geometry
|
|
LLVector3 x_transformed_reflected = x_transformed;
|
|
if (reflected)
|
|
{
|
|
x_transformed_reflected *= -1.0;
|
|
}
|
|
|
|
// compute rotation
|
|
LLMatrix3 rotation_matrix;
|
|
rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
|
|
LLQuaternion quat_rotation = rotation_matrix.quaternion();
|
|
quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here.
|
|
LLVector3 euler_rotation;
|
|
quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
|
|
|
|
|
|
//
|
|
// build parameter block to construct this prim
|
|
//
|
|
|
|
LLSD object_params;
|
|
|
|
// create prim
|
|
|
|
// set volume params
|
|
LLVolumeParams volume_params;
|
|
volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
|
|
volume_params.setBeginAndEndS( 0.f, 1.f );
|
|
volume_params.setBeginAndEndT( 0.f, 1.f );
|
|
volume_params.setRatio ( 1, 1 );
|
|
volume_params.setShear ( 0, 0 );
|
|
U8 sculpt_type = LL_SCULPT_TYPE_MESH;
|
|
if (reflected)
|
|
{
|
|
sculpt_type |= LL_SCULPT_FLAG_MIRROR;
|
|
}
|
|
volume_params.setSculptID(mesh_asset, sculpt_type);
|
|
object_params["shape"] = volume_params.asLLSD();
|
|
|
|
object_params["material"] = LL_MCODE_WOOD;
|
|
|
|
object_params["group-id"] = gAgent.getGroupID();
|
|
object_params["pos"] = ll_sd_from_vector3(position);
|
|
object_params["rotation"] = ll_sd_from_quaternion(quat_rotation);
|
|
object_params["scale"] = ll_sd_from_vector3(scale);
|
|
object_params["name"] = mStack.front().name;
|
|
|
|
// load material from dae file
|
|
std::vector<LLTextureEntry> texture_entries = getMaterials(model, instance_geo);
|
|
object_params["facelist"] = LLSD::emptyArray();
|
|
for (int i = 0; i < texture_entries.size(); i++)
|
|
{
|
|
object_params["facelist"][i] = texture_entries[i].asLLSD();
|
|
}
|
|
|
|
// set extra parameters
|
|
LLSculptParams sculpt_params;
|
|
sculpt_params.setSculptTexture(mesh_asset);
|
|
sculpt_params.setSculptType(sculpt_type);
|
|
U8 buffer[MAX_OBJECT_PARAMS_SIZE+1];
|
|
LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE);
|
|
sculpt_params.pack(dp);
|
|
std::vector<U8> v(dp.getCurrentSize());
|
|
memcpy(&v[0], buffer, dp.getCurrentSize());
|
|
LLSD extra_parameter;
|
|
extra_parameter["extra_parameter"] = sculpt_params.mType;
|
|
extra_parameter["param_data"] = v;
|
|
object_params["extra_parameters"].append(extra_parameter);
|
|
|
|
mObjectList.append(object_params);
|
|
|
|
delete model;
|
|
|
|
LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->setStatusCreatingPrim(mStack.front().name);
|
|
|
|
return;
|
|
}
|
|
|
|
void LLImportCollada::uploadObjectAsset()
|
|
{
|
|
LLSD request;
|
|
request["objects"] = mObjectList;
|
|
|
|
std::string url = gAgent.getRegion()->getCapability("UploadObjectAsset");
|
|
LLHTTPClient::post(url, request, new LLHTTPClient::Responder());
|
|
}
|
|
|
|
|
|
|
|
void LLImportCollada::importFile(std::string filename)
|
|
{
|
|
if (mIsImporting)
|
|
{
|
|
llwarns << "Importer already running, import command for " << filename << " ignored" << llendl;
|
|
return;
|
|
}
|
|
|
|
LLFloaterReg::showInstance("import_collada");
|
|
LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->enableOK(TRUE);
|
|
|
|
|
|
mIsImporting = TRUE;
|
|
mDAE = new DAE;
|
|
mImportOrigin = gAgent.getPositionAgent() + LLVector3(0, 0, 2);
|
|
mSceneTransformation = LLMatrix4(); // identity
|
|
mFilename = filename;
|
|
mCreates = 0;
|
|
mObjectList = LLSD::emptyArray();
|
|
|
|
if (mDAE->open(mFilename) == NULL)
|
|
{
|
|
llwarns << "cannot open file " << mFilename << llendl;
|
|
endImport();
|
|
return;
|
|
}
|
|
|
|
LLImportColladaAssetCache::getInstance()->prepareForUpload(mDAE);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void LLImportCollada::assetsUploaded()
|
|
{
|
|
if (!mIsImporting)
|
|
{
|
|
// weird, we got a callback while not importing.
|
|
return;
|
|
}
|
|
|
|
daeDocument* doc = mDAE->getDoc(mFilename);
|
|
if (!doc)
|
|
{
|
|
llwarns << "can't find internal doc" << llendl;
|
|
endImport();
|
|
}
|
|
|
|
daeElement* root = doc->getDomRoot();
|
|
if (!root)
|
|
{
|
|
llwarns << "document has no root" << llendl;
|
|
endImport();
|
|
}
|
|
|
|
domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID())));
|
|
if (unit)
|
|
{
|
|
mSceneTransformation *= unit->getMeter();
|
|
}
|
|
|
|
domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP
|
|
domAsset::domUp_axis* up_axis =
|
|
daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID())));
|
|
if (up_axis)
|
|
{
|
|
up = up_axis->getValue();
|
|
}
|
|
|
|
if (up == UPAXISTYPE_X_UP)
|
|
{
|
|
LLMatrix4 rotation;
|
|
rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
|
|
|
|
mSceneTransformation = matrix_multiply(rotation, mSceneTransformation);
|
|
}
|
|
else if (up == UPAXISTYPE_Y_UP)
|
|
{
|
|
LLMatrix4 rotation;
|
|
rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f);
|
|
|
|
mSceneTransformation = matrix_multiply(rotation, mSceneTransformation);
|
|
}
|
|
// else Z_UP, which is our behavior
|
|
|
|
|
|
|
|
daeElement* scene = root->getDescendant("visual_scene");
|
|
if (!scene)
|
|
{
|
|
llwarns << "document has no visual_scene" << llendl;
|
|
endImport();
|
|
}
|
|
|
|
processElement(scene);
|
|
processNextElement();
|
|
}
|
|
|
|
void LLImportCollada::pushStack(daeElement* next_element, std::string name, LLMatrix4 transformation)
|
|
{
|
|
struct StackState new_state;
|
|
|
|
new_state.next_element = next_element;
|
|
new_state.name = name;
|
|
new_state.transformation = transformation;
|
|
|
|
mStack.push_front(new_state);
|
|
}
|
|
|
|
|
|
|
|
void LLImportCollada::popStack()
|
|
{
|
|
mStack.pop_front();
|
|
}
|
|
|
|
|
|
|
|
BOOL LLImportCollada::processElement(daeElement* element)
|
|
{
|
|
if (mStack.size() > 0)
|
|
{
|
|
mStack.front().next_element = getNextSibling(element);
|
|
}
|
|
|
|
domTranslate* translate = daeSafeCast<domTranslate>(element);
|
|
if (translate)
|
|
{
|
|
domFloat3 dom_value = translate->getValue();
|
|
|
|
LLMatrix4 translation;
|
|
translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
|
|
|
|
mStack.front().transformation = matrix_multiply(translation, mStack.front().transformation);
|
|
}
|
|
|
|
domRotate* rotate = daeSafeCast<domRotate>(element);
|
|
if (rotate)
|
|
{
|
|
domFloat4 dom_value = rotate->getValue();
|
|
|
|
LLMatrix4 rotation;
|
|
rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0));
|
|
|
|
mStack.front().transformation = matrix_multiply(rotation, mStack.front().transformation);
|
|
}
|
|
|
|
domScale* scale = daeSafeCast<domScale>(element);
|
|
if (scale)
|
|
{
|
|
domFloat3 dom_value = scale->getValue();
|
|
|
|
LLMatrix4 scaling;
|
|
scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
|
|
|
|
mStack.front().transformation = matrix_multiply(scaling, mStack.front().transformation);
|
|
}
|
|
|
|
domMatrix* matrix = daeSafeCast<domMatrix>(element);
|
|
if (matrix)
|
|
{
|
|
domFloat4x4 dom_value = matrix->getValue();
|
|
|
|
LLMatrix4 matrix_transform;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
for(int j = 0; j < 4; j++)
|
|
{
|
|
matrix_transform.mMatrix[i][j] = dom_value[i + j*4];
|
|
}
|
|
|
|
mStack.front().transformation = matrix_multiply(matrix_transform, mStack.front().transformation);
|
|
}
|
|
|
|
domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element);
|
|
if (instance_geo)
|
|
{
|
|
appendObjectAsset(instance_geo);
|
|
}
|
|
|
|
domNode* node = daeSafeCast<domNode>(element);
|
|
if (node)
|
|
{
|
|
pushStack(getFirstChild(element), getElementLabel(element), mStack.front().transformation);
|
|
}
|
|
|
|
domInstance_node* instance_node = daeSafeCast<domInstance_node>(element);
|
|
if (instance_node)
|
|
{
|
|
daeElement* instance = instance_node->getUrl().getElement();
|
|
if (instance)
|
|
{
|
|
pushStack(getFirstChild(instance), getElementLabel(instance), mStack.front().transformation);
|
|
}
|
|
}
|
|
|
|
domVisual_scene* scene = daeSafeCast<domVisual_scene>(element);
|
|
if (scene)
|
|
{
|
|
pushStack(getFirstChild(element), std::string("scene"), mSceneTransformation);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void LLImportCollada::processNextElement()
|
|
{
|
|
while(1)
|
|
{
|
|
if (mStack.size() == 0)
|
|
{
|
|
uploadObjectAsset();
|
|
endImport();
|
|
return;
|
|
}
|
|
|
|
daeElement *element = mStack.front().next_element;
|
|
|
|
if (element == NULL)
|
|
{
|
|
popStack();
|
|
}
|
|
else
|
|
{
|
|
processElement(element);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LLImportCollada::endImport()
|
|
{
|
|
LLFloaterReg::hideInstance("import_collada");
|
|
|
|
LLImportColladaAssetCache::getInstance()->endImport();
|
|
|
|
if (mDAE)
|
|
{
|
|
delete mDAE;
|
|
mDAE = NULL;
|
|
}
|
|
|
|
mIsImporting = FALSE;
|
|
}
|
|
|
|
|
|
/* static */
|
|
void LLImportCollada::onCommitOK(LLUICtrl*, void*)
|
|
{
|
|
LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("import_collada")->enableOK(FALSE);
|
|
|
|
LLImportColladaAssetCache::getInstance()->uploadAssets();
|
|
}
|
|
|
|
|
|
/* static */
|
|
void LLImportCollada::onCommitCancel(LLUICtrl*, void*)
|
|
{
|
|
getInstance()->endImport();
|
|
}
|
|
|