]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/brushxml.h
reformat code! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / radiant / brushxml.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #if !defined( INCLUDED_BRUSHXML_H )
23 #define INCLUDED_BRUSHXML_H
24
25 #include "stream/stringstream.h"
26 #include "xml/xmlelement.h"
27
28 #include "brush.h"
29
30 inline void FaceTexdef_BP_importXML(FaceTexdef &texdef, const char *xmlContent)
31 {
32     StringTokeniser content(xmlContent);
33
34     texdef.m_projection.m_brushprimit_texdef.coords[0][0] = static_cast<float>( atof(content.getToken()));
35     texdef.m_projection.m_brushprimit_texdef.coords[0][1] = static_cast<float>( atof(content.getToken()));
36     texdef.m_projection.m_brushprimit_texdef.coords[0][2] = static_cast<float>( atof(content.getToken()));
37     texdef.m_projection.m_brushprimit_texdef.coords[1][0] = static_cast<float>( atof(content.getToken()));
38     texdef.m_projection.m_brushprimit_texdef.coords[1][1] = static_cast<float>( atof(content.getToken()));
39     texdef.m_projection.m_brushprimit_texdef.coords[1][2] = static_cast<float>( atof(content.getToken()));
40 }
41
42 inline void FaceTexdef_importXML(FaceTexdef &texdef, const char *xmlContent)
43 {
44     StringTokeniser content(xmlContent);
45
46     texdef.m_projection.m_texdef.shift[0] = static_cast<float>( atof(content.getToken()));
47     texdef.m_projection.m_texdef.shift[1] = static_cast<float>( atof(content.getToken()));
48     texdef.m_projection.m_texdef.rotate = static_cast<float>( atof(content.getToken()));
49     texdef.m_projection.m_texdef.scale[0] = static_cast<float>( atof(content.getToken()));
50     texdef.m_projection.m_texdef.scale[1] = static_cast<float>( atof(content.getToken()));
51
52     ASSERT_MESSAGE(texdef_sane(texdef.m_projection.m_texdef), "FaceTexdef_importXML: bad texdef");
53 }
54
55 inline void FacePlane_importXML(FacePlane &facePlane, const char *xmlContent)
56 {
57     StringTokeniser content(xmlContent);
58
59     for (int i = 0; i < 3; ++i) {
60         for (int j = 0; j < 3; ++j) {
61             facePlane.planePoints()[i][j] = atof(content.getToken());
62         }
63     }
64     facePlane.MakePlane();
65 }
66
67
68 class FaceXMLImporter {
69     struct xml_state_t {
70         enum EState {
71             eDefault,
72             ePlanePts,
73             eTexdef,
74             eBPMatrix,
75             eFlags,
76             eShader,
77         };
78
79         EState m_state;
80         StringOutputStream m_content;
81
82         xml_state_t(EState state)
83                 : m_state(state)
84         {}
85
86         EState state() const
87         {
88             return m_state;
89         }
90
91         const char *content() const
92         {
93             return m_content.c_str();
94         }
95
96         std::size_t write(const char *buffer, std::size_t length)
97         {
98             return m_content.write(buffer, length);
99         }
100     };
101
102     std::vector<xml_state_t> m_xml_state;
103     Face &m_face;
104 public:
105     FaceXMLImporter(Face &face) : m_face(face)
106     {
107         m_xml_state.push_back(xml_state_t::eDefault);
108     }
109
110     ~FaceXMLImporter()
111     {
112         m_face.planeChanged();
113     }
114
115     void pushElement(const XMLElement &element)
116     {
117         ASSERT_MESSAGE(m_xml_state.back().state() == xml_state_t::eDefault, "parse error");
118
119         if (strcmp(element.name(), "planepts") == 0) {
120             m_xml_state.push_back(xml_state_t::ePlanePts);
121         } else if (strcmp(element.name(), "texdef") == 0) {
122             m_xml_state.push_back(xml_state_t::eTexdef);
123         } else if (strcmp(element.name(), "bpmatrix") == 0) {
124             m_xml_state.push_back(xml_state_t::eBPMatrix);
125         } else if (strcmp(element.name(), "flags") == 0) {
126             m_xml_state.push_back(xml_state_t::eFlags);
127         } else if (strcmp(element.name(), "shader") == 0) {
128             m_xml_state.push_back(xml_state_t::eShader);
129         }
130     }
131
132     void popElement(const char *name)
133     {
134         ASSERT_MESSAGE(m_xml_state.back().state() != xml_state_t::eDefault, "parse error");
135
136         switch (m_xml_state.back().state()) {
137             case xml_state_t::ePlanePts: {
138                 FacePlane_importXML(m_face.getPlane(), m_xml_state.back().content());
139             }
140                 break;
141             case xml_state_t::eTexdef: {
142                 FaceTexdef_importXML(m_face.getTexdef(), m_xml_state.back().content());
143             }
144                 break;
145             case xml_state_t::eBPMatrix: {
146                 FaceTexdef_BP_importXML(m_face.getTexdef(), m_xml_state.back().content());
147             }
148                 break;
149             case xml_state_t::eFlags: {
150                 StringTokeniser content(m_xml_state.back().content());
151
152                 m_face.getShader().m_flags.m_contentFlags = atoi(content.getToken());
153                 m_face.getShader().m_flags.m_surfaceFlags = atoi(content.getToken());
154                 m_face.getShader().m_flags.m_value = atoi(content.getToken());
155             }
156                 break;
157             case xml_state_t::eShader: {
158                 m_face.getShader().setShader(m_xml_state.back().content());
159             }
160                 break;
161             default:
162                 break;
163         }
164
165         m_xml_state.pop_back();
166     }
167
168     std::size_t write(const char *data, std::size_t length)
169     {
170         ASSERT_MESSAGE(!m_xml_state.empty(), "parse error");
171         return m_xml_state.back().write(data, length);
172     }
173 };
174
175
176 inline void FaceTexdef_exportXML(const FaceTexdef &texdef, XMLImporter &importer)
177 {
178     StaticElement element("texdef");
179     importer.pushElement(element);
180
181     ASSERT_MESSAGE(texdef_sane(texdef.m_projection.m_texdef), "FaceTexdef_exportXML: bad texdef");
182
183     importer << texdef.m_projection.m_texdef.shift[0]
184              << ' ' << texdef.m_projection.m_texdef.shift[1]
185              << ' ' << texdef.m_projection.m_texdef.rotate
186              << ' ' << texdef.m_projection.m_texdef.scale[0]
187              << ' ' << texdef.m_projection.m_texdef.scale[1];
188
189     importer.popElement(element.name());
190 }
191
192 inline void FaceTexdef_BP_exportXML(const FaceTexdef &texdef, XMLImporter &importer)
193 {
194     StaticElement element("texdef");
195     importer.pushElement(element);
196
197     for (int i = 0; i < 2; ++i) {
198         for (int j = 0; j < 3; ++j) {
199             importer << texdef.m_projection.m_brushprimit_texdef.coords[i][j] << ' ';
200         }
201     }
202
203     importer.popElement(element.name());
204 }
205
206 inline void FaceShader_ContentsFlagsValue_exportXML(const FaceShader &faceShader, XMLImporter &importer)
207 {
208     StaticElement element("flags");
209     importer.pushElement(element);
210
211     {
212         importer << faceShader.m_flags.m_contentFlags
213                  << ' ' << faceShader.m_flags.m_surfaceFlags
214                  << ' ' << faceShader.m_flags.m_value;
215     }
216
217     importer.popElement(element.name());
218 }
219
220 inline void FacePlane_exportXML(const FacePlane &facePlane, XMLImporter &importer)
221 {
222     StaticElement element("planepts");
223     importer.pushElement(element);
224
225     {
226         // write planepts
227         for (int i = 0; i < 3; i++) {
228             for (int j = 0; j < 3; j++) {
229                 importer << Face::m_quantise(facePlane.planePoints()[i][j]) << ' ';
230             }
231         }
232     }
233
234     importer.popElement(element.name());
235 }
236
237 inline void FacePolygon_exportXML(const Winding &w, const BasicVector3<double> &normal, XMLImporter &importer)
238 {
239     DynamicElement element("polygon");
240
241     char tmp[32];
242
243     sprintf(tmp, "%f", normal.x());
244     element.insertAttribute("nx", tmp);
245
246     sprintf(tmp, "%f", normal.y());
247     element.insertAttribute("ny", tmp);
248
249     sprintf(tmp, "%f", normal.z());
250     element.insertAttribute("nz", tmp);
251
252     importer.pushElement(element);
253
254     for (unsigned int i = 0; i < w.numpoints; ++i) {
255         DynamicElement c("vertex");
256
257         sprintf(tmp, "%f", w.points[i].vertex.x());
258         c.insertAttribute("x", tmp);
259
260         sprintf(tmp, "%f", w.points[i].vertex.y());
261         c.insertAttribute("y", tmp);
262
263         sprintf(tmp, "%f", w.points[i].vertex.z());
264         c.insertAttribute("z", tmp);
265
266         sprintf(tmp, "%f", w.points[i].texcoord.x());
267         c.insertAttribute("s", tmp);
268
269         sprintf(tmp, "%f", w.points[i].texcoord.y());
270         c.insertAttribute("t", tmp);
271
272         importer.pushElement(c);
273         importer.popElement(c.name());
274     }
275
276     importer.popElement(element.name());
277 }
278
279 class FaceXMLExporter {
280     const Face &m_face;
281 public:
282     FaceXMLExporter(const Face &face) : m_face(face)
283     {
284     }
285
286     void exportXML(XMLImporter &importer)
287     {
288         bool bAlternateTexdef = (Face::m_type == eBrushTypeQuake3BP || Face::m_type == eBrushTypeDoom3 ||
289                                  Face::m_type == eBrushTypeQuake4);
290
291         // write shader
292         {
293             StaticElement element("shader");
294             importer.pushElement(element);
295             importer << m_face.getShader().getShader();
296             importer.popElement(element.name());
297         }
298
299         FacePolygon_exportXML(m_face.getWinding(), m_face.getPlane().plane3().normal(), importer);
300         FacePlane_exportXML(m_face.getPlane(), importer);
301
302         if (!bAlternateTexdef) {
303             FaceTexdef_exportXML(m_face.getTexdef(), importer);
304         } else {
305             FaceTexdef_BP_exportXML(m_face.getTexdef(), importer);
306         }
307
308         FaceShader_ContentsFlagsValue_exportXML(m_face.getShader(), importer);
309     }
310 };
311
312
313 class BrushXMLImporter : public XMLImporter {
314     class xml_state_t {
315     public:
316         enum EState {
317             eDefault,
318             eBrush,
319             eFace,
320         };
321
322     private:
323         EState m_state;
324
325     public:
326         xml_state_t(EState state)
327                 : m_state(state)
328         {
329         }
330
331         EState state() const
332         {
333             return m_state;
334         }
335     };
336
337     std::vector<xml_state_t> m_xml_state;
338     char m_faceImporter[sizeof(FaceXMLImporter)];
339     Brush &m_brush;
340
341     FaceXMLImporter &faceImporter()
342     {
343         return *reinterpret_cast<FaceXMLImporter *>( m_faceImporter );
344     }
345
346 public:
347     BrushXMLImporter(Brush &brush) : m_brush(brush)
348     {
349         m_xml_state.push_back(xml_state_t::eDefault);
350     }
351
352     void pushElement(const XMLElement &element)
353     {
354         switch (m_xml_state.back().state()) {
355             case xml_state_t::eDefault:
356                 ASSERT_MESSAGE(strcmp(element.name(), "brush") == 0, "parse error");
357                 m_xml_state.push_back(xml_state_t::eBrush);
358                 break;
359             case xml_state_t::eBrush:
360                 ASSERT_MESSAGE(strcmp(element.name(), "plane") == 0, "parse error");
361                 m_xml_state.push_back(xml_state_t::eFace);
362                 m_brush.push_back(FaceSmartPointer(new Face(&m_brush)));
363                 constructor(faceImporter(), makeReference(*m_brush.back()));
364                 m_brush.planeChanged();
365                 m_brush.shaderChanged();
366                 break;
367             case xml_state_t::eFace:
368                 m_xml_state.push_back(xml_state_t::eFace);
369                 faceImporter().pushElement(element);
370                 break;
371         }
372     }
373
374     void popElement(const char *name)
375     {
376         ASSERT_MESSAGE(!m_xml_state.empty(), "parse error");
377         m_xml_state.pop_back();
378
379         switch (m_xml_state.back().state()) {
380             case xml_state_t::eDefault:
381                 break;
382             case xml_state_t::eBrush:
383                 destructor(faceImporter());
384                 break;
385             case xml_state_t::eFace:
386                 faceImporter().popElement(name);
387                 break;
388         }
389     }
390
391     std::size_t write(const char *data, std::size_t length)
392     {
393         switch (m_xml_state.back().state()) {
394             case xml_state_t::eFace:
395                 return faceImporter().write(data, length);
396                 break;
397             default:
398                 break;
399         }
400         return length;
401     }
402 };
403
404 class BrushXMLExporter : public XMLExporter {
405     const Brush &m_brush;
406
407 public:
408     BrushXMLExporter(const Brush &brush) : m_brush(brush)
409     {
410     }
411
412     void exportXML(XMLImporter &importer)
413     {
414         m_brush.evaluateBRep(); // ensure b-rep is up-to-date, so that non-contributing faces can be identified.
415         ASSERT_MESSAGE(m_brush.hasContributingFaces(), "exporting an empty brush");
416
417         const StaticElement brushElement("brush");
418         importer.pushElement(brushElement);
419
420         for (Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i) {
421             if ((*i)->contributes()) {
422                 const StaticElement element("plane");
423                 importer.pushElement(element);
424                 FaceXMLExporter(*(*i)).exportXML(importer);
425                 importer.popElement(element.name());
426             }
427         }
428
429         importer.popElement(brushElement.name());
430     }
431 };
432
433
434 #endif