Added Wavefront OBJ format exporter
Exporter written by Apelsin: https://github.com/Apelsin TODO: Hookup avatar export
This commit is contained in:
@@ -248,6 +248,11 @@ protected:
|
||||
polymesh_map_t mPolyMeshes;
|
||||
avatar_joint_list_t mMeshLOD;
|
||||
|
||||
// <edit>
|
||||
public:
|
||||
const virtual avatar_joint_list_t& getMeshLOD() const { return mMeshLOD; }
|
||||
// </edit>
|
||||
|
||||
/** Meshes
|
||||
** **
|
||||
*******************************************************************************/
|
||||
|
||||
@@ -131,6 +131,10 @@ public:
|
||||
|
||||
void setIsTransparent(BOOL is_transparent) { mIsTransparent = is_transparent; }
|
||||
|
||||
// <edit>
|
||||
public:
|
||||
LLFace* getFace() { return mFace; }
|
||||
// </edit>
|
||||
private:
|
||||
// Allocate skin data
|
||||
BOOL allocateSkinData( U32 numSkinJoints );
|
||||
|
||||
@@ -86,6 +86,7 @@ set(viewer_SOURCE_FILES
|
||||
ascentprefschat.cpp
|
||||
ascentprefssys.cpp
|
||||
ascentprefsvan.cpp
|
||||
awavefront.cpp
|
||||
chatbar_as_cmdline.cpp
|
||||
floaterao.cpp
|
||||
floaterlocalassetbrowse.cpp
|
||||
@@ -597,6 +598,7 @@ set(viewer_HEADER_FILES
|
||||
ascentprefschat.h
|
||||
ascentprefssys.h
|
||||
ascentprefsvan.h
|
||||
awavefront.h
|
||||
chatbar_as_cmdline.h
|
||||
floaterao.h
|
||||
floaterlocalassetbrowse.h
|
||||
|
||||
@@ -642,5 +642,16 @@
|
||||
<key>Value</key>
|
||||
<boolean>0</boolean>
|
||||
</map>
|
||||
<key>OBJExportSwapYZ</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>OBJ Files saved swap Y and Z axes (useful for Cinema 4D users)</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
</map>
|
||||
</llsd>
|
||||
|
||||
391
indra/newview/awavefront.cpp
Normal file
391
indra/newview/awavefront.cpp
Normal file
@@ -0,0 +1,391 @@
|
||||
/**
|
||||
* @file awavefront.cpp
|
||||
* @brief A system which allows saving in-world objects to Wavefront .OBJ files for offline texturizing/shading.
|
||||
* @author Apelsin
|
||||
*
|
||||
* $LicenseInfo:firstyear=2011&license=LGPLV3$
|
||||
* Copyright (C) 2011-2013 Apelsin
|
||||
*
|
||||
* 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 "awavefront.h"
|
||||
|
||||
// library includes
|
||||
#include "aifilepicker.h"
|
||||
|
||||
// newview includes
|
||||
#include "llavatarappearancedefines.h"
|
||||
#include "llface.h"
|
||||
#include "llvoavatar.h"
|
||||
|
||||
typedef std::vector<LLAvatarJoint*> avatar_joint_list_t;
|
||||
|
||||
// menu includes
|
||||
#include "llevent.h"
|
||||
#include "llmemberlistener.h"
|
||||
#include "llview.h"
|
||||
#include "llselectmgr.h"
|
||||
|
||||
LLVOAvatar* find_avatar_from_object(LLViewerObject* object);
|
||||
|
||||
typedef LLMemberListener<LLView> view_listener_t;
|
||||
|
||||
namespace
|
||||
{
|
||||
const std::string OBJ(".obj");
|
||||
void save_wavefront_continued(WavefrontSaver* wfsaver, AIFilePicker* filepicker)
|
||||
{
|
||||
if (filepicker->hasFilename())
|
||||
{
|
||||
const std::string selected_filename = filepicker->getFilename();
|
||||
if (LLFILE* fp = LLFile::fopen(selected_filename, "wb"))
|
||||
{
|
||||
wfsaver->saveFile(fp);
|
||||
llinfos << "OBJ file saved to " << selected_filename << llendl;
|
||||
fclose(fp);
|
||||
}
|
||||
else llerrs << "can't open: " << selected_filename << llendl;
|
||||
}
|
||||
else llwarns << "No file; bailing" << llendl;
|
||||
|
||||
delete wfsaver;
|
||||
}
|
||||
}
|
||||
|
||||
Wavefront::Wavefront(vert_t v, tri_t t)
|
||||
: name("")
|
||||
, vertices(v)
|
||||
, triangles(t)
|
||||
{
|
||||
}
|
||||
|
||||
Wavefront::Wavefront(const LLVolumeFace* face, const LLXform* transform, const LLXform* transform_normals)
|
||||
: name("")
|
||||
{
|
||||
class v4adapt
|
||||
{
|
||||
private:
|
||||
LLStrider<LLVector4a> mV4aStrider;
|
||||
public:
|
||||
v4adapt(LLVector4a* vp){ mV4aStrider = vp; }
|
||||
inline LLVector3 operator[] (const unsigned int i)
|
||||
{
|
||||
return LLVector3((F32*)&mV4aStrider[i]);
|
||||
}
|
||||
};
|
||||
v4adapt verts(face->mPositions);
|
||||
for (S32 i = 0; i < face->mNumVertices; ++i)
|
||||
{
|
||||
LLVector3 v = verts[i];
|
||||
vertices.push_back(std::pair<LLVector3, LLVector2>(v, face->mTexCoords[i]));
|
||||
}
|
||||
|
||||
if (transform) Transform(vertices, transform);
|
||||
|
||||
v4adapt norms(face->mNormals);
|
||||
for (S32 i = 0; i < face->mNumVertices; ++i)
|
||||
normals.push_back(norms[i]);
|
||||
|
||||
if (transform_normals) Transform(normals, transform_normals);
|
||||
|
||||
for (S32 i = 0; i < face->mNumIndices/3; ++i)
|
||||
{
|
||||
triangles.push_back(tri(face->mIndices[i*3+0], face->mIndices[i*3+1], face->mIndices[i*3+2]));
|
||||
}
|
||||
}
|
||||
|
||||
Wavefront::Wavefront(LLFace* face, LLPolyMesh* mesh, const LLXform* transform, const LLXform* transform_normals)
|
||||
: name("")
|
||||
{
|
||||
LLVertexBuffer* vb = face->getVertexBuffer();
|
||||
if (!vb) return;
|
||||
|
||||
LLStrider<LLVector3> getVerts;
|
||||
LLStrider<LLVector3> getNorms;
|
||||
LLStrider<LLVector2> getCoord;
|
||||
LLStrider<U16> getIndices;
|
||||
face->getGeometry(getVerts, getNorms, getCoord, getIndices);
|
||||
|
||||
const U16 start = face->getGeomStart();
|
||||
const U32 end = start + (mesh ? mesh->getNumVertices() : vb->getNumVerts()) - 1; //vertices
|
||||
for (int i = start; i <= end; ++i)
|
||||
vertices.push_back(std::make_pair(getVerts[i], getCoord[i]));
|
||||
|
||||
if (transform) Transform(vertices, transform);
|
||||
|
||||
for (int i = start; i <= end; ++i)
|
||||
normals.push_back(getNorms[i]);
|
||||
|
||||
if (transform_normals) Transform(normals, transform_normals);
|
||||
|
||||
const U32 pcount = mesh ? mesh->getNumFaces() : (vb->getNumIndices()/3); //indices
|
||||
const U16 offset = face->getIndicesStart(); //indices
|
||||
for (int i = 0; i < pcount; ++i)
|
||||
{
|
||||
triangles.push_back(tri(getIndices[i * 3 + offset] + start, getIndices[i * 3 + 1 + offset] + start, getIndices[i * 3 + 2 + offset] + start));
|
||||
}
|
||||
}
|
||||
|
||||
void Wavefront::Transform(vert_t& v, const LLXform* x) //recursive
|
||||
{
|
||||
LLMatrix4 m;
|
||||
x->getLocalMat4(m);
|
||||
for (vert_t::iterator iterv = v.begin(); iterv != v.end(); ++iterv)
|
||||
{
|
||||
iterv->first = iterv->first * m;
|
||||
}
|
||||
|
||||
if (const LLXform* xp = x->getParent()) Transform(v, xp);
|
||||
}
|
||||
|
||||
void Wavefront::Transform(vec3_t& v, const LLXform* x) //recursive
|
||||
{
|
||||
LLMatrix4 m;
|
||||
x->getLocalMat4(m);
|
||||
for (vec3_t::iterator iterv = v.begin(); iterv != v.end(); ++iterv)
|
||||
{
|
||||
*iterv = *iterv * m;
|
||||
}
|
||||
|
||||
if (const LLXform* xp = x->getParent()) Transform(v, xp);
|
||||
}
|
||||
|
||||
WavefrontSaver::WavefrontSaver()
|
||||
{}
|
||||
|
||||
void WavefrontSaver::Add(const Wavefront& obj)
|
||||
{
|
||||
obj_v.push_back(obj);
|
||||
}
|
||||
|
||||
void WavefrontSaver::Add(const LLVolume* vol, const LLXform* transform, const LLXform* transform_normals)
|
||||
{
|
||||
const int faces = vol->getNumVolumeFaces();
|
||||
for(int i = 0; i < faces; ++i) //each face will be treated as a separate Wavefront object
|
||||
{
|
||||
Add(Wavefront(&vol->getVolumeFace(i), transform, transform_normals));
|
||||
}
|
||||
}
|
||||
void WavefrontSaver::Add(const LLViewerObject* some_vo)
|
||||
{
|
||||
LLXform v_form;
|
||||
v_form.setScale(some_vo->getScale());
|
||||
v_form.setPosition(some_vo->getRenderPosition());
|
||||
v_form.setRotation(some_vo->getRenderRotation());
|
||||
|
||||
LLXform normfix;
|
||||
normfix.setRotation(v_form.getRotation()); //Should work...
|
||||
Add(some_vo->getVolume(), &v_form, &normfix);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class LLSaveSelectedObjects : public view_listener_t
|
||||
{
|
||||
bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
|
||||
{
|
||||
if (LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection())
|
||||
{
|
||||
WavefrontSaver* wfsaver = new WavefrontSaver; //deleted in callback
|
||||
wfsaver->offset = -selection->getFirstRootObject()->getRenderPosition();
|
||||
for (LLObjectSelection::iterator iter = selection->begin(); iter != selection->end(); ++iter)
|
||||
{
|
||||
LLSelectNode* node = *iter;
|
||||
wfsaver->Add(node->getObject());
|
||||
}
|
||||
|
||||
AIFilePicker* filepicker = AIFilePicker::create();
|
||||
filepicker->open(selection->getFirstNode()->mName.c_str()+OBJ);
|
||||
filepicker->run(boost::bind(&save_wavefront_continued, wfsaver, filepicker));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void WavefrontSaver::Add(const LLVOAvatar* av_vo) //adds attachments, too!
|
||||
{
|
||||
offset = -av_vo->getRenderPosition();
|
||||
avatar_joint_list_t vjv = av_vo->getMeshLOD();
|
||||
for (avatar_joint_list_t::const_iterator itervj = vjv.begin(); itervj != vjv.end(); ++itervj)
|
||||
{
|
||||
const LLViewerJoint* vj = dynamic_cast<LLViewerJoint*>(*itervj);
|
||||
if (!vj || vj->mMeshParts.empty()) continue;
|
||||
|
||||
LLViewerJointMesh* vjm = dynamic_cast<LLViewerJointMesh*>(vj->mMeshParts[0]); //highest LOD
|
||||
if (!vjm) continue;
|
||||
|
||||
vjm->updateJointGeometry();
|
||||
LLFace* face = vjm->getFace();
|
||||
if (!face) continue;
|
||||
|
||||
//Beware: this is a hack because LLFace has multiple LODs
|
||||
//'pm' supplies the right number of vertices and triangles!
|
||||
LLPolyMesh* pm = vjm->getMesh();
|
||||
if (!pm) continue;
|
||||
LLXform normfix;
|
||||
normfix.setRotation(pm->getRotation());
|
||||
|
||||
//Special case for balls...I mean eyeballs!
|
||||
static const std::string eyeLname = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getMeshEntry(LLAvatarAppearanceDefines::MESH_ID_EYEBALL_LEFT)->mName;
|
||||
static const std::string eyeRname = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getMeshEntry(LLAvatarAppearanceDefines::MESH_ID_EYEBALL_RIGHT)->mName;
|
||||
const std::string name = vj->getName();
|
||||
if (name == eyeLname || name == eyeRname)
|
||||
{
|
||||
LLXform lol;
|
||||
lol.setPosition(-offset);
|
||||
Add(Wavefront(face, pm, &lol, &normfix));
|
||||
}
|
||||
else
|
||||
Add(Wavefront(face, pm, NULL, &normfix));
|
||||
}
|
||||
|
||||
for (LLVOAvatar::attachment_map_t::const_iterator iter = av_vo->mAttachmentPoints.begin(); iter != av_vo->mAttachmentPoints.end(); ++iter)
|
||||
{
|
||||
LLViewerJointAttachment* ja = iter->second;
|
||||
if (!ja) continue;
|
||||
|
||||
for (LLViewerJointAttachment::attachedobjs_vec_t::iterator itero = ja->mAttachedObjects.begin(); itero != ja->mAttachedObjects.end(); ++itero)
|
||||
{
|
||||
LLViewerObject* o = *itero;
|
||||
if (!o) continue;
|
||||
|
||||
LLDynamicArray<LLViewerObject*> prims = LLDynamicArray<LLViewerObject*>();
|
||||
o->addThisAndAllChildren(prims);
|
||||
for (LLDynamicArray<LLViewerObject*>::iterator iterc = prims.begin(); iterc != prims.end(); ++iterc)
|
||||
{
|
||||
const LLViewerObject* c = *iterc;
|
||||
if (!c) continue;
|
||||
const LLVolume* vol = c->getVolume();
|
||||
if (!vol) continue;
|
||||
|
||||
LLXform v_form;
|
||||
v_form.setScale(c->getScale());
|
||||
v_form.setPosition(c->getRenderPosition());
|
||||
v_form.setRotation(c->getRenderRotation());
|
||||
|
||||
LLXform normfix;
|
||||
normfix.setRotation(v_form.getRotation());
|
||||
|
||||
if (c->isHUDAttachment())
|
||||
{
|
||||
v_form.addPosition(-offset);
|
||||
//Normals of HUD elements are funky
|
||||
//TO-DO: fix 'em!
|
||||
}
|
||||
Add(vol, &v_form, &normfix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class LLSaveSelectedAvatar : public view_listener_t
|
||||
{
|
||||
bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
|
||||
{
|
||||
if (const LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()))
|
||||
{
|
||||
WavefrontSaver* wfsaver = new WavefrontSaver; //deleted in callback
|
||||
wfsaver->Add(avatar);
|
||||
|
||||
AIFilePicker* filepicker = AIFilePicker::create();
|
||||
filepicker->open(avatar->getFullname()+OBJ);
|
||||
filepicker->run(boost::bind(save_wavefront_continued, wfsaver, filepicker));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void write_or_bust(LLFILE* fp, const std::string outstring)
|
||||
{
|
||||
const size_t size = outstring.length();
|
||||
if (fwrite(outstring.c_str(), 1, size, fp) != size)
|
||||
llwarns << "Short write" << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
bool WavefrontSaver::saveFile(LLFILE* fp)
|
||||
{
|
||||
if (!fp) return false;
|
||||
|
||||
int num = 0;
|
||||
int index = 0;
|
||||
for (std::vector<Wavefront>::iterator w_iter = obj_v.begin(); w_iter != obj_v.end(); ++w_iter)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
std::string name = (*w_iter).name;
|
||||
if (name.empty()) name = llformat("%d", num++);
|
||||
|
||||
vert_t vertices = (*w_iter).vertices;
|
||||
vec3_t normals = (*w_iter).normals;
|
||||
tri_t triangles = (*w_iter).triangles;
|
||||
|
||||
//Write Object
|
||||
write_or_bust(fp, "o " + name + "\n");
|
||||
|
||||
//Write vertices; swap axes if necessary
|
||||
static const LLCachedControl<bool> swapYZ("OBJExportSwapYZ", false);
|
||||
const double xm = swapYZ ? -1.0 : 1.0;
|
||||
const int y = swapYZ ? 2 : 1;
|
||||
const int z = swapYZ ? 1 : 2;
|
||||
for (vert_t::iterator v_iter = vertices.begin(); v_iter != vertices.end(); ++v_iter)
|
||||
{
|
||||
++count;
|
||||
const LLVector3 v = v_iter->first + offset;
|
||||
write_or_bust(fp, llformat("v %f %f %f\n",v[0] * xm, v[y], v[z]));
|
||||
}
|
||||
|
||||
for (vec3_t::iterator n_iter = normals.begin(); n_iter != normals.end(); ++n_iter)
|
||||
{
|
||||
const LLVector3 n = *n_iter;
|
||||
write_or_bust(fp, llformat("vn %f %f %f\n",n[0] * xm, n[y], n[z]));
|
||||
}
|
||||
|
||||
for (vert_t::iterator v_iter = vertices.begin(); v_iter != vertices.end(); ++v_iter)
|
||||
{
|
||||
write_or_bust(fp, llformat("vt %f %f\n", v_iter->second[0], v_iter->second[1]));
|
||||
}
|
||||
|
||||
//Write triangles
|
||||
for (tri_t::iterator t_iter = triangles.begin(); t_iter != triangles.end(); ++t_iter)
|
||||
{
|
||||
const int f1 = t_iter->v0 + index + 1;
|
||||
const int f2 = t_iter->v1 + index + 1;
|
||||
const int f3 = t_iter->v2 + index + 1;
|
||||
write_or_bust(fp, llformat("f %d/%d/%d %d/%d/%d %d/%d/%d\n",
|
||||
f1,f1,f1,f2,f2,f2,f3,f3,f3));
|
||||
}
|
||||
index += count;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void addMenu(view_listener_t* menu, const std::string& name);
|
||||
void add_wave_listeners() //Called in llviewermenu with other addMenu calls, function linked against
|
||||
{
|
||||
addMenu(new LLSaveSelectedObjects(), "Object.SaveAsOBJ");
|
||||
addMenu(new LLSaveSelectedAvatar(), "Avatar.SaveAsOBJ");
|
||||
}
|
||||
|
||||
80
indra/newview/awavefront.h
Normal file
80
indra/newview/awavefront.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @file awavefront.h
|
||||
* @brief A system which allows saving in-world objects to Wavefront .OBJ files for offline texturizing/shading.
|
||||
* @author Apelsin
|
||||
*
|
||||
* $LicenseInfo:firstyear=2011&license=LGPLV3$
|
||||
* Copyright (C) 2011-2013 Apelsin
|
||||
*
|
||||
* 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 AWAVEFRONT
|
||||
#define AWAVEFRONT
|
||||
|
||||
#include <vector>
|
||||
#include "v3math.h"
|
||||
#include "v2math.h"
|
||||
#include "llface.h"
|
||||
#include "llvolume.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class LLFace;
|
||||
class LLPolyMesh;
|
||||
class LLViewerObject;
|
||||
class LLVOAvatar;
|
||||
|
||||
typedef std::vector<std::pair<LLVector3, LLVector2> > vert_t;
|
||||
typedef std::vector<LLVector3> vec3_t;
|
||||
|
||||
struct tri
|
||||
{
|
||||
tri(int a, int b, int c) : v0(a), v1(b), v2(c) {}
|
||||
int v0;
|
||||
int v1;
|
||||
int v2;
|
||||
};
|
||||
typedef std::vector<tri> tri_t;
|
||||
|
||||
class Wavefront
|
||||
{
|
||||
public:
|
||||
vert_t vertices;
|
||||
vec3_t normals; //null unless otherwise specified!
|
||||
tri_t triangles; //because almost all surfaces in SL are triangles
|
||||
std::string name;
|
||||
Wavefront(vert_t v, tri_t t);
|
||||
Wavefront(const LLVolumeFace* face, const LLXform* transform = NULL, const LLXform* transform_normals = NULL);
|
||||
Wavefront(LLFace* face, LLPolyMesh* mesh = NULL, const LLXform* transform = NULL, const LLXform* transform_normals = NULL);
|
||||
static void Transform(vert_t& v, const LLXform* x); //helper function
|
||||
static void Transform(vec3_t& v, const LLXform* x); //helper function
|
||||
};
|
||||
|
||||
class WavefrontSaver
|
||||
{
|
||||
public:
|
||||
std::vector<Wavefront> obj_v;
|
||||
LLVector3 offset;
|
||||
WavefrontSaver();
|
||||
void Add(const Wavefront& obj);
|
||||
void Add(const LLVolume* vol, const LLXform* transform = NULL, const LLXform* transform_normals = NULL);
|
||||
void Add(const LLViewerObject* some_vo);
|
||||
void Add(const LLVOAvatar* av_vo);
|
||||
bool saveFile(LLFILE* fp);
|
||||
};
|
||||
|
||||
#endif // AWAVEFRONT
|
||||
|
||||
@@ -171,6 +171,7 @@
|
||||
#include "shfloatermediaticker.h"
|
||||
#include "llpacketring.h"
|
||||
#include "aihttpview.h"
|
||||
#include "awavefront.h"
|
||||
// </edit>
|
||||
|
||||
#include "scriptcounter.h"
|
||||
@@ -488,6 +489,11 @@ void handle_mesh_save_obj(void*);
|
||||
void handle_mesh_load_obj(void*);
|
||||
void handle_morph_save_obj(void*);
|
||||
void handle_morph_load_obj(void*);
|
||||
void save_avatar_to_obj(LLVOAvatar *avatar);
|
||||
void save_selected_avatar_to_obj();
|
||||
void save_selected_objects_to_obj();
|
||||
void save_wavefront_continued(WavefrontSaver* wfsaver, AIFilePicker* filepicker);
|
||||
void handle_save_current_avatar_obj(void*);
|
||||
void handle_debug_avatar_textures(void*);
|
||||
void handle_dump_region_object_cache(void*);
|
||||
|
||||
@@ -700,7 +706,6 @@ void init_menus()
|
||||
gAttachSubMenu = gMenuBarView->getChildMenuByName("Attach Object", TRUE);
|
||||
gDetachSubMenu = gMenuBarView->getChildMenuByName("Detach Object", TRUE);
|
||||
|
||||
// TomY TODO convert these two
|
||||
LLMenuGL*menu;
|
||||
|
||||
// <dogmode>
|
||||
@@ -6132,6 +6137,24 @@ class LLAvatarInviteToGroup : public view_listener_t
|
||||
}
|
||||
};
|
||||
|
||||
class LLAvatarSaveAsOBJ : public view_listener_t
|
||||
{
|
||||
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
|
||||
{
|
||||
save_selected_avatar_to_obj();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class LLSelectionSaveAsOBJ : public view_listener_t
|
||||
{
|
||||
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
|
||||
{
|
||||
save_selected_objects_to_obj();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class LLAvatarAddFriend : public view_listener_t
|
||||
{
|
||||
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
|
||||
@@ -8515,6 +8538,104 @@ static void handle_morph_load_obj_continued(void* data, AIFilePicker* filepicker
|
||||
morph_data->setMorphFromMesh(&mesh);
|
||||
}
|
||||
|
||||
void handle_save_current_avatar_obj(void* data)
|
||||
{
|
||||
if(gAgentAvatarp)
|
||||
save_avatar_to_obj(gAgentAvatarp);
|
||||
}
|
||||
|
||||
void save_selected_avatar_to_obj()
|
||||
{
|
||||
LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() );
|
||||
if(avatar)
|
||||
save_avatar_to_obj(avatar);
|
||||
}
|
||||
|
||||
void save_avatar_to_obj(LLVOAvatar *avatar)
|
||||
{
|
||||
std::string file_name = llformat("%s.obj", avatar->getFullname().c_str());
|
||||
std::string full_path = gDirUtilp->getExpandedFilename(LL_PATH_NONE, file_name);
|
||||
|
||||
WavefrontSaver* wfsaver = new WavefrontSaver();
|
||||
wfsaver->Add((LLVOAvatar*)avatar);
|
||||
|
||||
AIFilePicker* filepicker = AIFilePicker::create();
|
||||
filepicker->open(full_path);
|
||||
filepicker->run(boost::bind(&save_wavefront_continued, wfsaver, filepicker));
|
||||
}
|
||||
|
||||
void save_selected_objects_to_obj()
|
||||
{
|
||||
struct ff : public LLSelectedNodeFunctor
|
||||
{
|
||||
virtual bool apply(LLSelectNode* node)
|
||||
{
|
||||
return LLObjectBackup::getInstance()->validatePerms(node->mPermissions);
|
||||
}
|
||||
} func;
|
||||
|
||||
LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection();
|
||||
|
||||
if(!selection)
|
||||
return;
|
||||
|
||||
if (!selection->applyToNodes(&func, false))
|
||||
{
|
||||
llwarns << "Incorrect permissions: Wavefront OBJ export aborted" << llendl;
|
||||
LLSD args;
|
||||
args["REASON"] = "Insufficient Permissions";
|
||||
LLNotificationsUtil::add("WavefrontExportFailed", args);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string file_name = llformat("%s.obj", selection->getFirstNode()->mName.c_str());
|
||||
std::string full_path = gDirUtilp->getExpandedFilename(LL_PATH_NONE, file_name);
|
||||
|
||||
WavefrontSaver* wfsaver = new WavefrontSaver();
|
||||
LLSelectNode* root_one = (LLSelectNode *)*selection->root_begin();
|
||||
wfsaver->offset = -root_one->getObject()->getRenderPosition();
|
||||
for (LLObjectSelection::iterator iter = selection->begin();
|
||||
iter != selection->end(); iter++)
|
||||
{
|
||||
LLSelectNode* node = *iter;
|
||||
LLViewerObject* object = node->getObject();
|
||||
wfsaver->Add(object);
|
||||
}
|
||||
|
||||
AIFilePicker* filepicker = AIFilePicker::create();
|
||||
filepicker->open(full_path);
|
||||
filepicker->run(boost::bind(&save_wavefront_continued, wfsaver, filepicker));
|
||||
}
|
||||
|
||||
void save_wavefront_continued(WavefrontSaver* wfsaver, AIFilePicker* filepicker)
|
||||
{
|
||||
if (!filepicker->hasFilename())
|
||||
{
|
||||
llwarns << "No file; bailing" << llendl;
|
||||
return;
|
||||
}
|
||||
std::string selected_filename = filepicker->getFilename();
|
||||
LLFILE* fp = LLFile::fopen(selected_filename, "wb");
|
||||
if (!fp)
|
||||
{
|
||||
llerrs << "can't open: " << selected_filename << llendl;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
wfsaver->saveFile(fp);
|
||||
}
|
||||
catch(int e)
|
||||
{
|
||||
llwarns << "An exception occurred while generating / saving OBJ file. Exception #" << e << llendl;
|
||||
}
|
||||
llinfos << "OBJ file saved to " << selected_filename << llendl;
|
||||
LLSD args;
|
||||
args["FILENAME"] = selected_filename;
|
||||
LLNotificationsUtil::add("WavefrontExportSuccess", args);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing.
|
||||
// Returns NULL on failure.
|
||||
LLVOAvatar* find_avatar_from_object( LLViewerObject* object )
|
||||
@@ -9388,6 +9509,7 @@ void initialize_menus()
|
||||
addMenu(new LLAvatarEnableFreezeEject(), "Avatar.EnableFreezeEject");
|
||||
addMenu(new LLAvatarCopyUUID(), "Avatar.CopyUUID");
|
||||
addMenu(new LLAvatarClientUUID(), "Avatar.ClientID");
|
||||
addMenu(new LLAvatarSaveAsOBJ(), "Avatar.SaveAsOBJ");
|
||||
|
||||
// Object pie menu
|
||||
addMenu(new LLObjectOpen(), "Object.Open");
|
||||
@@ -9419,7 +9541,8 @@ void initialize_menus()
|
||||
addMenu(new LLObjectExport(), "Object.Export");
|
||||
addMenu(new LLObjectImport(), "Object.Import");
|
||||
addMenu(new LLObjectImportUpload(), "Object.ImportUpload");
|
||||
|
||||
addMenu(new LLSelectionSaveAsOBJ(), "Object.SaveAsOBJ");
|
||||
|
||||
|
||||
addMenu(new LLObjectEnableOpen(), "Object.EnableOpen");
|
||||
addMenu(new LLObjectEnableTouch(), "Object.EnableTouch");
|
||||
|
||||
@@ -93,6 +93,9 @@ public:
|
||||
// Export result flags for textures.
|
||||
U32 mNonExportedTextures;
|
||||
|
||||
// Is exporting these objects allowed
|
||||
bool validatePerms(const LLPermissions* item_permissions);
|
||||
|
||||
private:
|
||||
// Static singleton stuff
|
||||
LLObjectBackup();
|
||||
@@ -103,7 +106,6 @@ private:
|
||||
void updateExportNumbers();
|
||||
|
||||
// Permissions stuff.
|
||||
bool validatePerms(const LLPermissions* item_permissions);
|
||||
LLUUID validateTextureID(LLUUID asset_id);
|
||||
|
||||
// Convert a selection list of objects to LLSD
|
||||
|
||||
@@ -60,13 +60,20 @@
|
||||
<menu_item_call enabled="true" hidden="false" label="Data" mouse_opaque="true" name="Data">
|
||||
<on_click function="Object.Data" />
|
||||
</menu_item_call>
|
||||
<menu_item_call enabled="false" hidden="false" label="Export" mouse_opaque="true" name="Export">
|
||||
<on_click function="Object.Export" />
|
||||
<on_enable function="Object.EnableExport" />
|
||||
</menu_item_call>
|
||||
<menu_item_call enabled="true" label="Reload" mouse_opaque="true" name="Reload Textures">
|
||||
<on_click function="Object.ReloadTextures" />
|
||||
</menu_item_call>
|
||||
<menu_item_separator />
|
||||
<pie_menu label="Export >" name="Export Menu">
|
||||
<menu_item_call enabled="false" hidden="false" label="XML" mouse_opaque="true" name="ExportXML">
|
||||
<on_click function="Object.Export" />
|
||||
<on_enable function="Object.EnableExport" />
|
||||
</menu_item_call>
|
||||
<menu_item_call enabled="false" hidden="false" label="Wavefront" mouse_opaque="true" name="ExportOBJ">
|
||||
<on_click function="Object.SaveAsOBJ" />
|
||||
<on_enable function="Object.EnableExport" />
|
||||
</menu_item_call>
|
||||
</pie_menu>
|
||||
</pie_menu>
|
||||
<menu_item_call enabled="false" label="Mute" mouse_opaque="true" name="Object Mute">
|
||||
<on_click function="Object.Mute" />
|
||||
|
||||
@@ -9637,4 +9637,22 @@ Would you like to enable announcing keys to objects in the sim?
|
||||
canceltext="No, don't ask again!"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alert.tga"
|
||||
name="WavefrontExportFailed"
|
||||
type="alert">
|
||||
Export to Wavefront OBJ file failed:
|
||||
|
||||
[REASON]
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="notify.tga"
|
||||
name="WavefrontExportSuccess"
|
||||
type="notify">
|
||||
Object successfully exported in Wavefront OBJ format to:
|
||||
|
||||
[FILENAME]
|
||||
</notification>
|
||||
|
||||
</notifications>
|
||||
|
||||
Reference in New Issue
Block a user