]> icculus.org git repositories - divverent/netradiant.git/blob - contrib/brushexport/export.cpp
NOW I do it right: #woxblox#
[divverent/netradiant.git] / contrib / brushexport / export.cpp
1 #include "export.h"
2 #include "debugging/debugging.h"
3 #include "ibrush.h"
4 #include "iscenegraph.h"
5 #include "iselection.h"
6 #include "stream/stringstream.h"
7 #include "stream/textfilestream.h"
8
9 #include <map>
10
11 // this is very evil, but right now there is no better way
12 #include "../../radiant/brush.h"
13
14 // for limNames
15 #define MAX_MATERIAL_NAME 20
16
17 /*
18         Abstract baseclass for modelexporters
19         the class collects all the data which then gets
20         exported through the WriteToFile method.
21 */
22 class ExportData
23 {
24 public:
25         ExportData(const std::set<std::string>& ignorelist, collapsemode mode, bool limNames, bool objs);
26         virtual ~ExportData(void);
27         
28         virtual void BeginBrush(Brush& b);
29         virtual void AddBrushFace(Face& f);
30         virtual void EndBrush(void);
31         
32         virtual bool WriteToFile(const std::string& path, collapsemode mode) const = 0;
33         
34 protected:
35
36         // a group of faces
37         class group
38         {
39         public:
40                 std::string name;
41                 std::list<const Face*> faces;
42         };
43         
44         std::list<group> groups;
45         
46 private:
47
48         // "textures/common/caulk" -> "caulk"
49         void GetShaderNameFromShaderPath(const char* path, std::string& name);
50
51         group* current; 
52         collapsemode mode;
53         const std::set<std::string>& ignorelist;
54 };
55
56 ExportData::ExportData(const std::set<std::string>& _ignorelist, collapsemode _mode, bool _limNames, bool _objs)
57         :       mode(_mode),
58                 ignorelist(_ignorelist)
59 {
60         current = 0;
61         
62         // in this mode, we need just one group
63         if(mode == COLLAPSE_ALL)
64         {
65                 groups.push_back(group());
66                 current = &groups.back();
67                 current->name = "all";
68         }
69 }
70
71 ExportData::~ExportData(void)
72 {
73         
74 }
75
76 void ExportData::BeginBrush(Brush& b)
77 {
78         // create a new group for each brush
79         if(mode == COLLAPSE_NONE)
80         {
81                 groups.push_back(group());
82                 current = &groups.back();
83                 
84                 StringOutputStream str(256);
85                 str << "Brush" << (const unsigned int)groups.size();
86                 current->name = str.c_str();
87         }
88 }
89
90 void ExportData::EndBrush(void)
91 {
92         // all faces of this brush were on the ignorelist, discard the emptygroup
93         if(mode == COLLAPSE_NONE)
94         {
95                 ASSERT_NOTNULL(current);
96                 if(current->faces.empty())
97                 {
98                         groups.pop_back();
99                         current = 0;
100                 }
101         }
102 }
103
104 void ExportData::AddBrushFace(Face& f)
105 {
106         std::string shadername;
107         GetShaderNameFromShaderPath(f.GetShader(), shadername);
108         
109         // ignore faces from ignore list
110         if(ignorelist.find(shadername) != ignorelist.end())
111                 return;
112                 
113         if(mode == COLLAPSE_BY_MATERIAL)
114         {
115                 // find a group for this material
116                 current = 0;
117                 const std::list<group>::iterator end(groups.end());
118                 for(std::list<group>::iterator it(groups.begin()); it != end; ++it)
119                 {
120                         if(it->name == shadername)
121                                 current = &(*it);
122                 }
123                 
124                 // no group found, create one
125                 if(!current)
126                 {
127                         groups.push_back(group());
128                         current = &groups.back();
129                         current->name = shadername;
130                 }
131         }
132         
133         ASSERT_NOTNULL(current);
134         
135         // add face to current group
136         current->faces.push_back(&f);
137         
138 #ifdef _DEBUG
139         globalOutputStream() << "Added Face to group " << current->name.c_str() << "\n";
140 #endif
141 }
142
143 void ExportData::GetShaderNameFromShaderPath(const char* path, std::string& name)
144 {
145         std::string tmp(path);
146
147         size_t last_slash = tmp.find_last_of("/");
148         
149         if(last_slash != std::string::npos && last_slash == (tmp.length() - 1))
150                 name = path;
151         else
152                 name = tmp.substr(last_slash + 1, tmp.length() - last_slash);
153
154 #ifdef _DEBUG
155         globalOutputStream() << "Last: " << (const unsigned int) last_slash << " " << "length: " << (const unsigned int)tmp.length() << "Name: " << name.c_str() << "\n";
156 #endif
157 }
158
159 /*
160         Exporter writing facedata as wavefront object
161 */
162 class ExportDataAsWavefront : public ExportData
163 {
164 private:
165         bool expmat;
166         bool limNames;
167         bool objs;
168
169 public:
170         ExportDataAsWavefront(const std::set<std::string>& _ignorelist, collapsemode _mode, bool _expmat, bool _limNames, bool _objs)
171                 : ExportData(_ignorelist, _mode, _limNames, _objs)
172         {
173                 expmat = _expmat;
174                 limNames = _limNames;
175                 objs = _objs;
176         }
177         
178         bool WriteToFile(const std::string& path, collapsemode mode) const;
179 };
180
181 bool ExportDataAsWavefront::WriteToFile(const std::string& path, collapsemode mode) const
182 {
183         std::string objFile = path;
184
185         if(path.compare(path.length() - 4, 4, ".obj") != 0)
186                 objFile += ".obj";
187
188         std::string mtlFile = objFile.substr(0, objFile.length() -4) + ".mtl";
189
190         std::set<std::string> materials;
191
192         TextFileOutputStream out(objFile.c_str());
193         
194         if(out.failed())
195         {
196                 globalErrorStream() << "Unable to open file\n";
197                 return false;
198         }
199                 
200         out << "# Wavefront Objectfile exported with radiants brushexport plugin 3.0 by Thomas 'namespace' Nitschke, spam@codecreator.net\n\n";
201
202         if(expmat)
203         {
204                 size_t last = mtlFile.find_last_of("//");
205                 std::string mtllib = mtlFile.substr(last + 1, mtlFile.size() - last).c_str();
206                 out << "mtllib " << mtllib.c_str() << "\n";
207         }
208         
209         unsigned int vertex_count = 0;
210
211         const std::list<ExportData::group>::const_iterator gend(groups.end());
212         for(std::list<ExportData::group>::const_iterator git(groups.begin()); git != gend; ++git)
213         {
214                 typedef std::multimap<std::string, std::string> bm;
215                 bm brushMaterials;
216                 typedef std::pair<std::string, std::string> String_Pair;
217
218                 const std::list<const Face*>::const_iterator end(git->faces.end());
219                 
220                 // submesh starts here
221                 if(objs)
222                 {
223                         out << "\no ";
224                 } else {
225                         out << "\ng ";
226                 }
227                 out << git->name.c_str() << "\n";
228
229                 // material
230                 if(expmat && mode == COLLAPSE_ALL)
231                 {
232                         out << "usemtl material" << "\n\n";
233                         materials.insert("material");
234                 }
235
236                 for(std::list<const Face*>::const_iterator it(git->faces.begin()); it != end; ++it)
237                 {
238                         const Winding& w((*it)->getWinding());
239                         
240                         // vertices
241                         for(size_t i = 0; i < w.numpoints; ++i)
242                                         out << "v " << FloatFormat(w[i].vertex.x(), 1, 6) << " " << FloatFormat(w[i].vertex.z(), 1, 6) << " " << FloatFormat(w[i].vertex.y(), 1, 6) << "\n";
243                 }
244                 out << "\n";    
245                 
246                 for(std::list<const Face*>::const_iterator it(git->faces.begin()); it != end; ++it)
247                 {
248                         const Winding& w((*it)->getWinding());
249                         
250                         // texcoords
251                         for(size_t i = 0; i < w.numpoints; ++i)
252                                 out << "vt " << FloatFormat(w[i].texcoord.x(), 1, 6) << " " << FloatFormat(w[i].texcoord.y(), 1, 6) << "\n";
253                 }
254                 
255                 for(std::list<const Face*>::const_iterator it(git->faces.begin()); it != end; ++it)
256                 {
257                         const Winding& w((*it)->getWinding());
258                         
259                         // faces
260                         StringOutputStream faceLine(256);
261                         faceLine << "\nf";
262                         for(size_t i = 0; i < w.numpoints; ++i, ++vertex_count)
263                         {
264                                 faceLine << " " << vertex_count+1 << "/" << vertex_count+1;
265                         }
266
267                         if(mode != COLLAPSE_ALL)
268                         {
269                                 materials.insert((*it)->getShader().getShader());
270                                 brushMaterials.insert(String_Pair((*it)->getShader().getShader(), faceLine.c_str()));
271                         } else {
272                                 out << faceLine.c_str();
273                         }
274                 }
275
276                 if(mode != COLLAPSE_ALL)
277                 {
278                         std::string lastMat;
279                         std::string mat;
280                         std::string faces;
281
282                         for(bm::iterator iter = brushMaterials.begin(); iter != brushMaterials.end(); iter++)
283                         {
284                                 mat = (*iter).first.c_str();
285                                 faces = (*iter).second.c_str();
286
287                                 if(mat != lastMat)
288                                 {
289                                         if(limNames && mat.size() > MAX_MATERIAL_NAME)
290                                         {
291                                                 out << "\nusemtl " << mat.substr(mat.size() - MAX_MATERIAL_NAME, mat.size()).c_str();
292                                         } else {
293                                                 out << "\nusemtl " << mat.c_str();
294                                         }
295                                 }
296
297                                 out << faces.c_str();
298                                 lastMat = mat;
299                         }
300                 }
301
302                 out << "\n";
303         }
304
305         if(expmat)
306         {
307                 TextFileOutputStream outMtl(mtlFile.c_str());
308                 if(outMtl.failed())
309                 {
310                         globalErrorStream() << "Unable to open material file\n";
311                         return false;
312                 }
313
314                 outMtl << "# Wavefront material file exported with NetRadiants brushexport plugin.\n";
315                 outMtl << "# Material Count: " << (const Unsigned)materials.size() << "\n\n";
316                 for(std::set<std::string>::const_iterator it(materials.begin()); it != materials.end(); ++it)
317                 {
318                         if(limNames && it->size() > MAX_MATERIAL_NAME)
319                         {
320                                 outMtl << "newmtl " << it->substr(it->size() - MAX_MATERIAL_NAME, it->size()).c_str() << "\n";
321                         } else {
322                                 outMtl << "newmtl " << it->c_str() << "\n";
323                         }
324                 }
325         }
326         
327         return true;
328 }
329
330
331 class ForEachFace : public BrushVisitor
332 {
333 public:
334         ForEachFace(ExportData& _exporter)
335                 : exporter(_exporter)
336         {}
337         
338         void visit(Face& face) const
339         {
340                 exporter.AddBrushFace(face);
341         }
342         
343 private:
344         ExportData& exporter;
345 };
346
347 class ForEachSelected : public SelectionSystem::Visitor
348 {
349 public:
350         ForEachSelected(ExportData& _exporter)
351                 : exporter(_exporter)
352         {}
353         
354         void visit(scene::Instance& instance) const
355     {
356                 BrushInstance* bptr = InstanceTypeCast<BrushInstance>::cast(instance);
357                 if(bptr)
358                 {
359                         Brush& brush(bptr->getBrush());
360                         
361                         exporter.BeginBrush(brush);
362                                 ForEachFace face_vis(exporter);
363                                 brush.forEachFace(face_vis);
364                         exporter.EndBrush();
365                 }
366     }
367     
368 private:
369         ExportData& exporter;
370 };
371  
372 bool ExportSelection(const std::set<std::string>& ignorelist, collapsemode m, bool exmat, const std::string& path, bool limNames, bool objs)
373 {
374         ExportDataAsWavefront exporter(ignorelist, m, exmat, limNames, objs);
375         
376         ForEachSelected vis(exporter);
377         GlobalSelectionSystem().foreachSelected(vis);
378         
379         return exporter.WriteToFile(path, m);
380 }