From 1c2cdd4166f89eb1bbe089a2d25c89fbe6e22fde Mon Sep 17 00:00:00 2001 From: Latif Khalifa Date: Mon, 15 Jul 2013 06:38:12 +0200 Subject: [PATCH] Work in progress: Collada export --- indra/newview/CMakeLists.txt | 2 + indra/newview/daeexport.cpp | 412 ++++++++++++++++++ indra/newview/daeexport.h | 47 ++ indra/newview/llviewermenu.cpp | 2 + .../default/xui/en-us/menu_pie_object.xml | 4 + 5 files changed, 467 insertions(+) create mode 100644 indra/newview/daeexport.cpp create mode 100644 indra/newview/daeexport.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b6639e5d4..58da1cacc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -88,6 +88,7 @@ set(viewer_SOURCE_FILES ascentprefsvan.cpp awavefront.cpp chatbar_as_cmdline.cpp + daeexport.cpp floaterao.cpp floaterlocalassetbrowse.cpp floatervoicelicense.cpp @@ -601,6 +602,7 @@ set(viewer_HEADER_FILES ascentprefsvan.h awavefront.h chatbar_as_cmdline.h + daeexport.h floaterao.h floaterlocalassetbrowse.h floatervoicelicense.h diff --git a/indra/newview/daeexport.cpp b/indra/newview/daeexport.cpp new file mode 100644 index 000000000..1333729fb --- /dev/null +++ b/indra/newview/daeexport.cpp @@ -0,0 +1,412 @@ +/** +* @file daeexport.cpp +* @brief A system which allows saving in-world objects to Collada .DAE files for offline texturizing/shading. +* @authors Latif Khalifa +* +* $LicenseInfo:firstyear=2011&license=LGPLV3$ +* Copyright (C) 2013 Latif Khalifa +* +* 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; either +* version 3 of the License, or (at your option) any later version. +* +* 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 */ + +#include "llviewerprecompiledheaders.h" + +#include "daeexport.h" + +//colladadom includes +#if LL_MSVC +#pragma warning (disable : 4018) +#pragma warning (push) +#pragma warning (disable : 4068) +#pragma warning (disable : 4263) +#pragma warning (disable : 4264) +#endif +#pragma GCC diagnostic ignored "-Woverloaded-virtual" +#include "dae.h" +//#include "dom.h" +#include "dom/domAsset.h" +#include "dom/domBind_material.h" +#include "dom/domCOLLADA.h" +#include "dom/domConstants.h" +#include "dom/domController.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" +#if LL_MSVC +#pragma warning (pop) +#endif + +// library includes +#include "aifilepicker.h" +#include "llnotificationsutil.h" +#include "boost/date_time/posix_time/posix_time.hpp" + +// newview includes +#include "lfsimfeaturehandler.h" +#include "llface.h" +#include "llvovolume.h" + +// menu includes +#include "llevent.h" +#include "llmemberlistener.h" +#include "llview.h" +#include "llselectmgr.h" + +extern LLUUID gAgentID; + +//Typedefs used in other files, using here for consistency. +typedef LLMemberListener view_listener_t; + +namespace DAEExportUtil +{ + void saveImpl(DAESaver* daesaver, AIFilePicker* filepicker) + { + if (filepicker->hasFilename()) + { + const std::string selected_filename = filepicker->getFilename(); + + daesaver->saveDAE(selected_filename); + LLNotificationsUtil::add("WavefrontExportSuccess", LLSD().with("FILENAME", selected_filename)); + } + else llwarns << "No file; bailing" << llendl; + + delete daesaver; + } + + void filePicker(DAESaver* daesaver, std::string name) + { + AIFilePicker* filepicker = AIFilePicker::create(); + filepicker->open(name); + filepicker->run(boost::bind(&saveImpl, daesaver, filepicker)); + } + + void onPatialExportConfirm(const LLSD& notification, const LLSD& response, DAESaver* daesaver, std::string name) + { + if (LLNotificationsUtil::getSelectedOption(notification, response) == 0) // 0 - Proceed, first choice + { + filePicker(daesaver, name); + } + else + { + delete daesaver; + } + } + + bool canExportNode(const LLSelectNode* node) + { + if (const LLPermissions* perms = node->mPermissions) + { + if (gAgentID == perms->getCreator() || (LFSimFeatureHandler::instance().simSupportsExport() && gAgentID == perms->getOwner() && perms->getMaskEveryone() & PERM_EXPORT)) + { + return true; + } + } + return false; + } + + void saveSelectedObject() + { + static const std::string file_ext = ".dae"; + + if (LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection()) + { + DAESaver* daesaver = new DAESaver; // deleted in callback + daesaver->mOffset = -selection->getFirstRootObject()->getRenderPosition(); + S32 total = 0; + S32 included = 0; + + for (LLObjectSelection::iterator iter = selection->begin(); iter != selection->end(); ++iter) + { + total++; + LLSelectNode* node = *iter; + if (!canExportNode(node)) continue; + included++; + daesaver->Add(node->getObject(), node->mName); + } + + if (daesaver->mObjects.empty()) + { + LLNotificationsUtil::add("ExportFailed"); + delete daesaver; + return; + } + + if (total != included) + { + LLSD args; + args["TOTAL"] = total; + args["FAILED"] = total - included; + LLNotificationsUtil::add("WavefrontExportPartial", args, LLSD(), boost::bind(&onPatialExportConfirm, _1, _2, daesaver, selection->getFirstNode()->mName.c_str() + file_ext)); + return; + } + + filePicker(daesaver, selection->getFirstNode()->mName.c_str() + file_ext); + } + return; + } + + class DAESaveSelectedObjects : public view_listener_t + { + bool handleEvent(LLPointer event, const LLSD& userdata) + { + saveSelectedObject(); + return true; + } + }; +} + + +void DAESaver::Add(const LLViewerObject* prim, const std::string name) +{ + mObjects.push_back(std::pair((LLViewerObject*)prim, name)); +} + +void DAESaver::DAESaveAccessor(domAccessor* acc, int numValues, std::string params) +{ + acc->setCount(numValues); + acc->setStride(params.size()); + + for (std::string::iterator p_iter = params.begin(); p_iter != params.end(); ++p_iter) + { + domElement* pX = acc->add("param"); + pX->setAttribute("name", llformat("%c", *p_iter).c_str()); + pX->setAttribute("type", "float"); + } +} + +class v4adapt +{ +private: + LLStrider mV4aStrider; +public: + v4adapt(LLVector4a* vp){ mV4aStrider = vp; } + inline LLVector3 operator[] (const unsigned int i) + { + return LLVector3((F32*)&mV4aStrider[i]); + } +}; + +bool DAESaver::saveDAE(std::string filename) +{ + DAE dae; + // First set the filename to save + daeElement* root = dae.add(filename); + + // Obligatory elements in header + daeElement* asset = root->add("asset"); + // Get ISO format time + std::string date = boost::posix_time::to_iso_extended_string(boost::posix_time::second_clock::local_time()); + daeElement* created = asset->add("created"); + created->setCharData(date); + daeElement* modified = asset->add("modified"); + modified->setCharData(date); + daeElement* unit = asset->add("unit"); + unit->setAttribute("name", "meter"); + unit->setAttribute("value", "1"); + daeElement* up_axis = asset->add("up_axis"); + up_axis->setCharData("Z_UP"); + + // File creator + daeElement* contributor = asset->add("contributor"); + contributor->add("author")->setCharData("Singularity User"); + contributor->add("authoring_tool")->setCharData("Singularity Viewer Collada Export"); + + daeElement* geomLib = root->add("library_geometries"); + daeElement* scene = root->add("library_visual_scenes visual_scene"); + scene->setAttribute("id", "Scene"); + scene->setAttribute("name", "Scene"); + S32 prim_nr = 0; + + for (obj_info_t::iterator obj_iter = mObjects.begin(); obj_iter != mObjects.end(); ++obj_iter) + { + LLViewerObject* obj = obj_iter->first; + S32 total_num_vertices = 0; + + std::string name = ""; + if (name.empty()) name = llformat("prim%d", prim_nr++); + + const char* geomID = name.c_str(); + + daeElement* geom = geomLib->add("geometry"); + geom->setAttribute("id", llformat("%s-%s", geomID, "mesh").c_str()); + daeElement* mesh = geom->add("mesh"); + + daeElement* positionsSource = mesh->add("source"); + positionsSource->setAttribute("id", llformat("%s-%s", geomID, "positions").c_str()); + daeElement* positionsArray = positionsSource->add("float_array"); + + daeElement* uvSource = mesh->add("source"); + uvSource->setAttribute("id", llformat("%s-%s", geomID, "map-0").c_str()); + daeElement* uvArray = uvSource->add("float_array"); + + daeElement* normalsSource = mesh->add("source"); + normalsSource->setAttribute("id", llformat("%s-%s", geomID, "normals").c_str()); + daeElement* normalsArray = normalsSource->add("float_array"); + + S32 num_faces = obj->getVolume()->getNumVolumeFaces(); + for (S32 face_num = 0; face_num < num_faces; face_num++) + { + const LLVolumeFace* face = (LLVolumeFace*)&obj->getVolume()->getVolumeFace(face_num); + total_num_vertices += face->mNumVertices; + + // Positions + v4adapt verts(face->mPositions); + for (S32 i=0; i < face->mNumVertices; i++) + { + ((domFloat_array*)positionsArray)->getValue().append3(verts[i].mV[VX], verts[i].mV[VY], verts[i].mV[VZ]); + } + + // UV map + for (S32 i=0; i < face->mNumVertices; i++) + { + ((domFloat_array*)uvArray)->getValue().append2(face->mTexCoords[i].mV[VX], face->mTexCoords[i].mV[VY]); + } + + + // Normals + v4adapt norms(face->mNormals); + for (S32 i=0; i < face->mNumVertices; i++) + { + const LLVector3 n = norms[i]; + ((domFloat_array*)normalsArray)->getValue().append3(n[0], n[1], n[2]); + + } + } + + // Save positions + positionsArray->setAttribute("id", llformat("%s-%s", geomID, "positions-array").c_str()); + positionsArray->setAttribute("count", llformat("%d", total_num_vertices * 3).c_str()); + domAccessor* positionsAcc = daeSafeCast(positionsSource->add("technique_common accessor")); + positionsAcc->setSource(llformat("#%s-%s", geomID, "positions-array").c_str()); + this->DAESaveAccessor(positionsAcc, total_num_vertices, "XYZ"); + + // Save UV map + uvArray->setAttribute("id", llformat("%s-%s", geomID, "map-0-array").c_str()); + uvArray->setAttribute("count", llformat("%d", total_num_vertices * 2).c_str()); + domAccessor* uvAcc = daeSafeCast(uvSource->add("technique_common accessor")); + uvAcc->setSource(llformat("#%s-%s", geomID, "map-0-array").c_str()); + this->DAESaveAccessor(uvAcc, total_num_vertices, "ST"); + + // Save normals + normalsArray->setAttribute("id", llformat("%s-%s", geomID, "normals-array").c_str()); + normalsArray->setAttribute("count", llformat("%d", total_num_vertices * 3).c_str()); + domAccessor* normalsAcc = daeSafeCast(normalsSource->add("technique_common accessor")); + normalsAcc->setSource(llformat("#%s-%s", geomID, "normals-array").c_str()); + DAESaveAccessor(normalsAcc, total_num_vertices, "XYZ"); + + // Add the element + { + daeElement* verticesNode = mesh->add("vertices"); + verticesNode->setAttribute("id", llformat("%s-%s", geomID, "vertices").c_str()); + daeElement* verticesInput = verticesNode->add("input"); + verticesInput->setAttribute("semantic", "POSITION"); + verticesInput->setAttribute("source", llformat("#%s-%s", geomID, "positions").c_str()); + } + + // Add triangles + domPolylist* polylist = daeSafeCast(mesh->add("polylist")); + polylist->setMaterial("mtl"); + + // Vertices semantic + { + domInputLocalOffset* input = daeSafeCast(polylist->add("input")); + input->setSemantic("VERTEX"); + input->setOffset(0); + input->setSource(llformat("#%s-%s", geomID, "vertices").c_str()); + } + + // Normals semantic + { + domInputLocalOffset* input = daeSafeCast(polylist->add("input")); + input->setSemantic("NORMAL"); + input->setOffset(0); + input->setSource(llformat("#%s-%s", geomID, "normals").c_str()); + } + + // UV semantic + { + domInputLocalOffset* input = daeSafeCast(polylist->add("input")); + input->setSemantic("TEXCOORD"); + input->setOffset(0); + input->setSource(llformat("#%s-%s", geomID, "map-0").c_str()); + } + + // Save indices + domP* p = daeSafeCast(polylist->add("p")); + domPolylist::domVcount *vcount = daeSafeCast(polylist->add("vcount")); + S32 index_offset = 0; + S32 num_tris = 0; + for (S32 face_num = 0; face_num < num_faces; face_num++) + { + const LLVolumeFace* face = (LLVolumeFace*)&obj->getVolume()->getVolumeFace(face_num); + for (S32 i = 0; i < face->mNumIndices; i++) + { + U16 index = index_offset + face->mIndices[i]; + (p->getValue()).append(index); + if (i % 3 == 0) + { + (vcount->getValue()).append(3); + num_tris++; + } + } + index_offset += face->mNumVertices; + } + polylist->setCount(num_tris); + + // Add scene + daeElement* node = scene->add("node"); + node->setAttribute("type", "NODE"); + node->setAttribute("id", geomID); + node->setAttribute("name", geomID); + + // Set tranform matrix (object position, rotation and scale) + domMatrix* matrix = (domMatrix*)node->add("matrix"); + LLXform srt; + srt.setScale(obj->getScale()); + srt.setPosition(obj->getRenderPosition() + mOffset); + srt.setRotation(obj->getRenderRotation()); + LLMatrix4 m4; + srt.getLocalMat4(m4); + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + (matrix->getValue()).append(m4.mMatrix[j][i]); + + daeElement* nodeGeometry = node->add("instance_geometry"); + nodeGeometry->setAttribute("url", llformat("#%s-%s", geomID, "mesh").c_str()); + } + root->add("scene instance_visual_scene")->setAttribute("url", "#Scene"); + + return dae.writeAll(); +} + +DAESaver::DAESaver() +{} + +void addMenu(view_listener_t* menu, const std::string& name); +void add_dae_listeners() // Called in llviewermenu with other addMenu calls, function linked against +{ + addMenu(new DAEExportUtil::DAESaveSelectedObjects(), "Object.SaveAsDAE"); +} diff --git a/indra/newview/daeexport.h b/indra/newview/daeexport.h new file mode 100644 index 000000000..145df0580 --- /dev/null +++ b/indra/newview/daeexport.h @@ -0,0 +1,47 @@ +/** +* @file daeexport.h +* @brief A system which allows saving in-world objects to Collada .DAE files for offline texturizing/shading. +* @authors Latif Khalifa +* +* $LicenseInfo:firstyear=2011&license=LGPLV3$ +* Copyright (C) 2013 Latif Khalifa +* +* 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; either +* version 3 of the License, or (at your option) any later version. +* +* 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 */ + +#ifndef DAEEXPORT_H_ +#define DAEEXPORT_H_ + +#include + +class LLViewerObject; + +class DAESaver +{ + typedef std::vector > obj_info_t; + +public: + obj_info_t mObjects; + LLVector3 mOffset; + DAESaver(); + void Add(const LLViewerObject* prim, const std::string name); + bool saveDAE(std::string filename); + +private: + void DAESaveAccessor(domAccessor* acc, int numValues, std::string params); +}; + +#endif // DAEEXPORT_H_ + diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a271f8a6b..3733c6311 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -225,6 +225,7 @@ void handle_test_load_url(void*); class AIHTTPView; void add_wave_listeners(); +void add_dae_listeners(); //extern BOOL gHideSelectedObjects; //extern BOOL gAllowSelectAvatar; //extern BOOL gDebugAvatarRotation; @@ -9409,6 +9410,7 @@ void initialize_menus() addMenu(new LLCanIHasKillEmAll(), "Object.EnableDestroy"); addMenu(new LLOHGOD(), "Object.EnableExplode"); add_wave_listeners(); + add_dae_listeners(); // addMenu(new LLObjectMute(), "Object.Mute"); addMenu(new LLObjectBuy(), "Object.Buy"); diff --git a/indra/newview/skins/default/xui/en-us/menu_pie_object.xml b/indra/newview/skins/default/xui/en-us/menu_pie_object.xml index 75fba2a28..940729207 100644 --- a/indra/newview/skins/default/xui/en-us/menu_pie_object.xml +++ b/indra/newview/skins/default/xui/en-us/menu_pie_object.xml @@ -68,6 +68,10 @@ + + + +