root/cafu/trunk/CaWE/LoadSave_cmap.cpp

Revision 469, 29.6 KB (checked in by Carsten, 4 months ago)

Model code:

  • Removed methods from class AnimPoseT that have been obsoleted by the AnimExpressionT classes.
  • Updated all user code accordingly.
Line 
1/*
2=================================================================================
3This file is part of Cafu, the open-source game engine and graphics engine
4for multiplayer, cross-platform, real-time 3D action.
5Copyright (C) 2002-2012 Carsten Fuchs Software.
6
7Cafu is free software: you can redistribute it and/or modify it under the terms
8of the GNU General Public License as published by the Free Software Foundation,
9either version 3 of the License, or (at your option) any later version.
10
11Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with Cafu. If not, see <http://www.gnu.org/licenses/>.
17
18For support and more information about Cafu, visit us at <http://www.cafu.de>.
19=================================================================================
20*/
21
22#include "EntityClass.hpp"
23#include "EntityClassVar.hpp"
24#include "LuaAux.hpp"
25#include "GameConfig.hpp"
26#include "MapBezierPatch.hpp"
27#include "MapBrush.hpp"
28#include "MapDocument.hpp"
29#include "MapEntity.hpp"
30#include "MapFace.hpp"
31#include "MapModel.hpp"
32#include "MapPlant.hpp"
33#include "MapTerrain.hpp"
34#include "EditorMaterial.hpp"
35#include "EditorMaterialManager.hpp"
36
37#include "Math3D/Plane3.hpp"
38#include "Models/Model_cmdl.hpp"
39#include "TextParser/TextParser.hpp"
40#include "SceneGraph/LightMapMan.hpp"
41#include "ClipSys/CollisionModelMan.hpp"
42#include "ClipSys/CollisionModel_base.hpp"
43
44#include "wx/wx.h"
45#include "wx/progdlg.h"
46
47
48static const double CAFU_ENG_SCALE=25.4;
49static const bool   WriteComments =false;
50static unsigned int MapFileVersion=0;
51
52
53/// This function serializes a given float f1 to a string s, such that:
54///   - s is minimal (uses the least number of decimal digits required),
55///   - unserializing s back to a float f2 yields f1==f2.
56/// See my post "float to string to float, with first float == second float"
57/// to comp.lang.c++ on 2009-10-06 for additional details.
58static std::string serialize(float f1)
59{
60    // From MSDN documentation: "digits10 returns the number of decimal digits that the type can represent without loss of precision."
61    // For floats, that's usually 6, for doubles, that's usually 15. However, we want to use the number of *significant* decimal digits here,
62    // that is, max_digits10. See http://www.open-std.org/JTC1/sc22/wg21/docs/papers/2006/n2005.pdf for details.
63    const unsigned int DIGITS10    =std::numeric_limits<float>::digits10;
64    const unsigned int MAX_DIGITS10=DIGITS10+3;
65
66    std::string  s;
67    unsigned int prec;
68
69    for (prec=DIGITS10; prec<=MAX_DIGITS10; prec++)
70    {
71        std::stringstream ss;
72
73        ss.precision(prec);
74        ss << f1;
75
76        s=ss.str();
77
78        float f2;
79        ss >> f2;
80
81        if (f2==f1) break;
82    }
83
84    wxASSERT(prec<=MAX_DIGITS10);
85    return s;
86}
87
88
89static std::string serialize(const Vector3fT& v)
90{
91    return serialize(v.x)+" "+serialize(v.y)+" "+serialize(v.z);
92}
93
94
95/***************************/
96/*** Load/Save functions ***/
97/***************************/
98
99void MapElementT::Load_cmap(TextParserT& TP, MapDocumentT& MapDoc)
100{
101    if (TP.PeekNextToken()=="Group")
102    {
103        TP.AssertAndSkipToken("Group");
104        const unsigned long GroupNr=TP.GetNextTokenAsInt();
105
106        if (GroupNr<MapDoc.GetGroups().Size())
107            SetGroup(MapDoc.GetGroups()[GroupNr]);
108    }
109}
110
111
112void MapElementT::Save_cmap(std::ostream& OutFile, unsigned long ElemNr, const MapDocumentT& MapDoc) const
113{
114    const int GroupNr=MapDoc.GetGroups().Find(m_Group);
115
116    if (GroupNr!=-1)
117        OutFile << "    Group " << GroupNr << "\n";
118}
119
120
121MapFaceT MapFaceT::Create_cmap(TextParserT& TP, EditorMatManT& MatMan)
122{
123    MapFaceT Face;
124
125    TP.AssertAndSkipToken("(");
126    Face.m_PlanePoints[0].x=TP.GetNextTokenAsFloat();
127    Face.m_PlanePoints[0].y=TP.GetNextTokenAsFloat();
128    Face.m_PlanePoints[0].z=TP.GetNextTokenAsFloat();
129    TP.AssertAndSkipToken(")");
130
131    TP.AssertAndSkipToken("(");
132    Face.m_PlanePoints[1].x=TP.GetNextTokenAsFloat();
133    Face.m_PlanePoints[1].y=TP.GetNextTokenAsFloat();
134    Face.m_PlanePoints[1].z=TP.GetNextTokenAsFloat();
135    TP.AssertAndSkipToken(")");
136
137    TP.AssertAndSkipToken("(");
138    Face.m_PlanePoints[2].x=TP.GetNextTokenAsFloat();
139    Face.m_PlanePoints[2].y=TP.GetNextTokenAsFloat();
140    Face.m_PlanePoints[2].z=TP.GetNextTokenAsFloat();
141    TP.AssertAndSkipToken(")");
142
143    std::string TexName=TP.GetNextToken();
144
145    if (MapFileVersion<=9)
146    {
147        // All faces below version 10 have no information about the creation of their texture
148        // coordinates mode and it is therefore initialized as plane projected.
149        Face.m_SurfaceInfo.TexCoordGenMode=PlaneProj;
150
151        // Normally this code would be sufficient, but it fails with TexName was empty and non-quoted.
152        // Face.m_SurfaceInfo.trans[0]=TP.GetNextTokenAsFloat();
153        // Face.m_SurfaceInfo.trans[1]=TP.GetNextTokenAsFloat();
154
155        // This code solves the problem.
156        Face.m_SurfaceInfo.Trans[0]=TP.GetNextTokenAsFloat();
157        std::string TestToken=TP.GetNextToken();
158        if (TestToken=="(")
159        {
160            // The texture name was apparently missing, so fix the problem now as well as possible.
161            Face.m_SurfaceInfo.Trans[1]=Face.m_SurfaceInfo.Trans[0];
162            Face.m_SurfaceInfo.Trans[0]=atof(TexName.c_str());
163            TexName="";
164            TP.PutBack(TestToken);
165        }
166        else Face.m_SurfaceInfo.Trans[1]=atof(TestToken.c_str());
167
168        TP.AssertAndSkipToken("(");
169        Face.m_SurfaceInfo.UAxis.x=TP.GetNextTokenAsFloat();
170        Face.m_SurfaceInfo.UAxis.y=TP.GetNextTokenAsFloat();
171        Face.m_SurfaceInfo.UAxis.z=TP.GetNextTokenAsFloat();
172        TP.AssertAndSkipToken(")");
173
174        TP.AssertAndSkipToken("(");
175        Face.m_SurfaceInfo.VAxis.x=TP.GetNextTokenAsFloat();
176        Face.m_SurfaceInfo.VAxis.y=TP.GetNextTokenAsFloat();
177        Face.m_SurfaceInfo.VAxis.z=TP.GetNextTokenAsFloat();
178        TP.AssertAndSkipToken(")");
179
180        Face.m_SurfaceInfo.Rotate=TP.GetNextTokenAsFloat();
181        TP.GetNextToken();      // ID of SmoothingGroup this face is in.
182    }
183    else
184    {
185        Face.m_SurfaceInfo=SurfaceInfoT::Create_cmap(TP);
186    }
187
188    if (MapFileVersion<=6)
189    {
190        // Scale the coordinates down.
191        Face.m_PlanePoints[0]/=CAFU_ENG_SCALE;
192        Face.m_PlanePoints[1]/=CAFU_ENG_SCALE;
193        Face.m_PlanePoints[2]/=CAFU_ENG_SCALE;
194    }
195
196    // Restore the scale values and renormalize the axes (when MapFileVersion<=9).
197    const float LenU=length(Face.m_SurfaceInfo.UAxis);
198    const float LenV=length(Face.m_SurfaceInfo.VAxis);
199
200    if (MapFileVersion<=9)
201    {
202        // Renormalize the axes.
203        Face.m_SurfaceInfo.UAxis/=LenU;
204        Face.m_SurfaceInfo.VAxis/=LenV;
205    }
206
207    Face.m_Plane   =Plane3fT(Face.m_PlanePoints[0], Face.m_PlanePoints[1], Face.m_PlanePoints[2], 0.1f);
208    Face.m_Material=MatMan.FindMaterial(TexName, true /*return dummy if not found*/);
209
210    if (MapFileVersion<=6)
211    {
212        Face.m_SurfaceInfo.Scale[0]=1.0/(LenU/CAFU_ENG_SCALE * Face.m_Material->GetWidth());
213        Face.m_SurfaceInfo.Scale[1]=1.0/(LenV/CAFU_ENG_SCALE * Face.m_Material->GetHeight());
214    }
215    else if (MapFileVersion<=8)
216    {
217        Face.m_SurfaceInfo.Scale[0]=1.0/(LenU * Face.m_Material->GetWidth());
218        Face.m_SurfaceInfo.Scale[1]=1.0/(LenV * Face.m_Material->GetHeight());
219    }
220    else if (MapFileVersion==9)
221    {
222        Face.m_SurfaceInfo.Scale[0]=1.0/LenU;
223        Face.m_SurfaceInfo.Scale[1]=1.0/LenV;
224    }
225
226    if (MapFileVersion<=8)
227    {
228        Face.m_SurfaceInfo.Trans[0]/=Face.m_Material->GetWidth();
229        Face.m_SurfaceInfo.Trans[1]/=Face.m_Material->GetHeight();
230    }
231
232    return Face;
233}
234
235
236void MapFaceT::Save_cmap(std::ostream& OutFile) const
237{
238    OutFile << "   "
239            << " ( " << serialize(m_PlanePoints[0]) << " )"
240            << " ( " << serialize(m_PlanePoints[1]) << " )"
241            << " ( " << serialize(m_PlanePoints[2]) << " )"
242            << " \"" << m_Material->GetName() << "\" ";
243
244    m_SurfaceInfo.Save_cmap(OutFile);
245}
246
247
248MapBrushT* MapBrushT::Create_cmap(TextParserT& TP, MapDocumentT& MapDoc, unsigned long EntityNr, unsigned long BrushNr)
249{
250    MapBrushT* Brush=new MapBrushT();
251
252    TP.AssertAndSkipToken("{");
253
254    Brush->Load_cmap(TP, MapDoc);   // The method of the MapElementT base class.
255    Brush->m_Faces.Clear();
256
257    while (true)
258    {
259        std::string Token=TP.GetNextToken();
260        TP.PutBack(Token);
261
262        if (Token!="(") break;
263
264        Brush->m_Faces.PushBack(MapFaceT::Create_cmap(TP, MapDoc.GetGameConfig()->GetMatMan()));
265    }
266
267    TP.AssertAndSkipToken("}");
268
269    Brush->CompleteFaceVertices();
270
271    if (!Brush->IsValid())
272    {
273        wxLogWarning("Entity %lu, primitive %lu: The brush could not be created from its planes.", EntityNr, BrushNr);
274    }
275
276    wxASSERT(Brush->IsValid());
277    return Brush;
278}
279
280
281void MapBrushT::Save_cmap(std::ostream& OutFile, unsigned long PrimitiveNr, const MapDocumentT& MapDoc) const
282{
283    // Only write the "// Primitive XY" comment every 20-th primitive.
284    // This keeps the diff smaller for cmap files that are under (Subversion) revision control,
285    // because deletions or insertions of primitives cause all subsequent brushes to get renumbered.
286    if ((PrimitiveNr % 20)==0 && WriteComments) OutFile << "\n  { // Primitive " << PrimitiveNr << "\n";
287                                           else OutFile << "\n  {\n";
288
289    MapElementT::Save_cmap(OutFile, PrimitiveNr, MapDoc);
290
291    for (unsigned long FaceNr=0; FaceNr<m_Faces.Size(); FaceNr++)
292        if (m_Faces[FaceNr].GetVertices().Size()>0)
293            m_Faces[FaceNr].Save_cmap(OutFile);
294
295    OutFile << "  }\n";
296}
297
298
299void MapBezierPatchT::Load_cmap(TextParserT& TP, MapDocumentT& MapDoc)
300{
301    TP.AssertAndSkipToken("PatchDef");
302    TP.AssertAndSkipToken("{");
303
304    MapElementT::Load_cmap(TP, MapDoc);
305
306    wxString texname=TP.GetNextToken();
307    SetMaterial(MapDoc.GetGameConfig()->GetMatMan().FindMaterial(texname, true /*Create dummy if not found.*/));
308
309    if (MapFileVersion<=9)
310    {
311        // All bezier patches below version 10 have no surface information and are therefore
312        // initialized with custom texture coordinates generation mode.
313        SurfaceInfo.TexCoordGenMode=Custom;
314    }
315    else
316    {
317        SurfaceInfo=SurfaceInfoT::Create_cmap(TP);
318    }
319
320    TP.AssertAndSkipToken("(");
321
322    unsigned long width =TP.GetNextTokenAsInt();
323    unsigned long height=TP.GetNextTokenAsInt();
324
325    SetSize(width, height);
326
327    SubdivsHorz=(MapFileVersion>=8) ? TP.GetNextTokenAsInt() : -1;
328    SubdivsVert=(MapFileVersion>=8) ? TP.GetNextTokenAsInt() : -1;
329
330    TP.AssertAndSkipToken(")");
331
332    for (unsigned long y=0; y<cv_Height; y++)
333    {
334        for (unsigned long x=0 ; x<cv_Width; x++)
335        {
336            TP.AssertAndSkipToken("(");
337
338            Vector3fT Pos;
339            Pos.x=TP.GetNextTokenAsFloat();
340            Pos.y=TP.GetNextTokenAsFloat();
341            Pos.z=TP.GetNextTokenAsFloat();
342            // Scaling was required for mapfile_version <= 6.
343            if (MapFileVersion<=6) Pos/=CAFU_ENG_SCALE;
344            SetCvPos(x, y, Pos);
345
346            Vector3fT TexCoord;
347            TexCoord.x=TP.GetNextTokenAsFloat();
348            TexCoord.y=TP.GetNextTokenAsFloat();
349            SetCvUV(x, y, TexCoord);
350
351            TP.AssertAndSkipToken(")");
352        }
353    }
354
355    TP.AssertAndSkipToken("}");
356}
357
358
359void MapBezierPatchT::Save_cmap(std::ostream& OutFile, unsigned long PrimitiveNr, const MapDocumentT& MapDoc) const
360{
361    // Only write the "// Primitive XY" comment every 20-th primitive.
362    // This keeps the diff smaller for cmap files that are under (Subversion) revision control,
363    // because deletions or insertions of primitives cause all subsequent brushes to get renumbered.
364    if ((PrimitiveNr % 20)==0 && WriteComments) OutFile << "\n" << "PatchDef    // Primitive " << PrimitiveNr << "\n" << "{\n";
365                                           else OutFile << "\n" << "PatchDef"                                 << "\n" << "{\n";
366
367    MapElementT::Save_cmap(OutFile, PrimitiveNr, MapDoc);
368
369    OutFile << "\"" << GetMaterial()->GetName() << "\"" << "\n";
370    SurfaceInfo.Save_cmap(OutFile);
371    OutFile << "( " << cv_Width << " " << cv_Height << " " << SubdivsHorz << " " << SubdivsVert << " )\n";
372
373    for (unsigned long y=0; y<cv_Height; y++)
374    {
375        for (unsigned long x=0; x<cv_Width; x++)
376        {
377            const Vector3fT& Pos     =GetCvPos(x, y);
378            const Vector3fT& TexCoord=GetCvUV (x, y);
379
380            // Note that since mapfile_version 7, we no longer scale the vertex coordinates by CAFU_ENG_SCALE.
381            OutFile << " ( " << serialize(Pos)
382                    << " " << serialize(TexCoord.x)
383                    << " " << serialize(TexCoord.y) << " )";
384        }
385
386        OutFile << "\n";
387    }
388
389    OutFile << "}\n";
390}
391
392
393void MapTerrainT::Load_cmap(TextParserT& TP, MapDocumentT& MapDoc)
394{
395    TP.AssertAndSkipToken("TerrainDef");
396    TP.AssertAndSkipToken("{");
397
398    MapElementT::Load_cmap(TP, MapDoc);
399
400    SetMaterial(MapDoc.GetGameConfig()->GetMatMan().FindMaterial(TP.GetNextToken(), true));
401
402    TP.AssertAndSkipToken("(");
403    m_TerrainBounds.Min.x=TP.GetNextTokenAsFloat();
404    m_TerrainBounds.Min.y=TP.GetNextTokenAsFloat();
405    m_TerrainBounds.Min.z=TP.GetNextTokenAsFloat();
406    TP.AssertAndSkipToken(")");
407
408    TP.AssertAndSkipToken("(");
409    m_TerrainBounds.Max.x=TP.GetNextTokenAsFloat();
410    m_TerrainBounds.Max.y=TP.GetNextTokenAsFloat();
411    m_TerrainBounds.Max.z=TP.GetNextTokenAsFloat();
412    TP.AssertAndSkipToken(")");
413
414    TP.AssertAndSkipToken("(");
415    m_Resolution=TP.GetNextTokenAsInt();
416    TP.AssertAndSkipToken(")");
417
418    m_HeightData.Clear();
419    m_HeightData.PushBackEmpty(m_Resolution*m_Resolution);
420
421    for (unsigned long y=0; y<m_Resolution; y++)
422        for (unsigned long x=0 ; x<m_Resolution; x++)
423            m_HeightData[y*m_Resolution+x]=TP.GetNextTokenAsInt();
424
425    TP.AssertAndSkipToken("}");
426    m_NeedsUpdate=true;
427}
428
429
430void MapTerrainT::Save_cmap(std::ostream& OutFile, unsigned long PrimitiveNr, const MapDocumentT& MapDoc) const
431{
432    // Only write the "// Primitive XY" comment every 20-th primitive.
433    // This keeps the diff smaller for cmap files that are under (Subversion) revision control,
434    // because deletions or insertions of primitives cause all subsequent brushes to get renumbered.
435    if ((PrimitiveNr % 20)==0 && WriteComments) OutFile << "\n" << "TerrainDef    // Primitive " << PrimitiveNr << "\n" << "{\n";
436                                           else OutFile << "\n" << "TerrainDef"                                 << "\n" << "{\n";
437
438    MapElementT::Save_cmap(OutFile, PrimitiveNr, MapDoc);
439
440    OutFile << \"" << m_Material->GetName() << "\"" << "\n";
441    OutFile << "  ( " << serialize(m_TerrainBounds.Min) << " )\n";
442    OutFile << "  ( " << serialize(m_TerrainBounds.Max) << " )\n";
443    OutFile << "  ( " << m_Resolution << " )" << "\n";
444
445    OutFile << "  ";
446
447    // Serialize the height data.
448    for (unsigned long i=1; i<=m_HeightData.Size(); i++)
449    {
450        OutFile << std::setfill(' ') << std::setw(5) << m_HeightData[i-1];
451        OutFile << (((i % 20)==0) ? "\n  " : " ");
452    }
453
454    OutFile << "\n}\n";
455}
456
457
458void MapPlantT::Load_cmap(TextParserT& TP, MapDocumentT& MapDoc)
459{
460    TP.AssertAndSkipToken("PlantDef");
461    TP.AssertAndSkipToken("{");
462
463    MapElementT::Load_cmap(TP, MapDoc);
464
465    m_DescrFileName=TP.GetNextToken();
466    m_RandomSeed   =TP.GetNextTokenAsInt();
467
468    TP.AssertAndSkipToken("(");
469    m_Position.x=TP.GetNextTokenAsFloat();
470    m_Position.y=TP.GetNextTokenAsFloat();
471    m_Position.z=TP.GetNextTokenAsFloat();
472    TP.AssertAndSkipToken(")");
473
474    TP.AssertAndSkipToken("(");
475    m_Angles[ROLL ]=TP.GetNextTokenAsFloat();
476    m_Angles[PITCH]=TP.GetNextTokenAsFloat();
477    m_Angles[YAW  ]=TP.GetNextTokenAsFloat();
478    TP.AssertAndSkipToken(")");
479
480    TP.AssertAndSkipToken("}");
481    m_Tree=TreeT(MapDoc.GetPlantDescrMan().GetPlantDescription(std::string(m_DescrFileName)), m_RandomSeed);
482}
483
484
485void MapPlantT::Save_cmap(std::ostream& OutFile, unsigned long PlantNr, const MapDocumentT& MapDoc) const
486{
487    OutFile << "\n" << "  PlantDef" << " { ";
488
489    MapElementT::Save_cmap(OutFile, PlantNr, MapDoc);
490
491    OutFile << "\"" << m_DescrFileName << "\"" << " ";
492    OutFile << m_RandomSeed << " ";
493    OutFile << "( " << serialize(m_Position) << " ) ";
494    OutFile << "( " << serialize(m_Angles[ROLL]) << " " << serialize(m_Angles[PITCH]) << " " << serialize(m_Angles[YAW]) << " ) ";
495
496    OutFile << "}\n";
497}
498
499
500void MapModelT::Load_cmap(TextParserT& TP, MapDocumentT& MapDoc)
501{
502    TP.AssertAndSkipToken("ModelDef");
503    TP.AssertAndSkipToken("{");
504
505    MapElementT::Load_cmap(TP, MapDoc);
506
507    m_ModelFileName    =TP.GetNextToken();
508    m_Model            =MapDoc.GetGameConfig()->GetModel(m_ModelFileName);
509    m_CollModelFileName=TP.GetNextToken();
510
511    m_Label=TP.GetNextToken();
512
513    TP.AssertAndSkipToken("(");
514    m_Origin.x=TP.GetNextTokenAsFloat();
515    m_Origin.y=TP.GetNextTokenAsFloat();
516    m_Origin.z=TP.GetNextTokenAsFloat();
517    TP.AssertAndSkipToken(")");
518
519    TP.AssertAndSkipToken("(");
520    m_Angles[ROLL ]=TP.GetNextTokenAsFloat();
521    m_Angles[PITCH]=TP.GetNextTokenAsFloat();
522    m_Angles[YAW  ]=TP.GetNextTokenAsFloat();
523    TP.AssertAndSkipToken(")");
524
525    m_Scale         =TP.GetNextTokenAsFloat();
526    const int SequNr=TP.GetNextTokenAsInt();
527    m_FrameOffset   =TP.GetNextTokenAsFloat();
528    m_FrameTimeScale=TP.GetNextTokenAsFloat();
529    m_Animated      =TP.GetNextTokenAsInt()!=0;
530
531    m_AnimExpr=m_Model->GetAnimExprPool().GetStandard(SequNr, m_FrameOffset);
532    TP.AssertAndSkipToken("}");
533}
534
535
536void MapModelT::Save_cmap(std::ostream& OutFile, unsigned long ModelNr, const MapDocumentT& MapDoc) const
537{
538    OutFile << "\n" << "  ModelDef" << " { ";
539
540    MapElementT::Save_cmap(OutFile, ModelNr, MapDoc);
541
542    OutFile << "\"" << m_ModelFileName     << "\" ";
543    OutFile << "\"" << m_CollModelFileName << "\" ";
544    OutFile << "\"" << m_Label             << "\" ";
545
546    OutFile << "( " << serialize(m_Origin) << " ) ";
547    OutFile << "( " << serialize(m_Angles[ROLL]) << " " << serialize(m_Angles[PITCH]) << " " << serialize(m_Angles[YAW]) << " ) ";
548
549    OutFile << serialize(m_Scale)          << " ";
550    OutFile << m_AnimExpr->GetSequNr()     << " ";
551    OutFile << serialize(m_FrameOffset)    << " ";
552    OutFile << serialize(m_FrameTimeScale) << " ";
553    OutFile << int(m_Animated)             << " ";
554
555    OutFile << "}\n";
556}
557
558
559void EntPropertyT::Load_cmap(TextParserT& TP)
560{
561    Key  =TP.GetNextToken();
562    Value=TP.GetNextToken();
563
564    if (Key=="angles")
565    {
566        const Vector3fT Angles=GetVector3f();
567
568        float Pitch=Angles.x;
569        float Yaw  =Angles.y;
570        float Roll =Angles.z;
571
572        // Angles get special treatment because we measure angles a little differently:
573        // Pitch      :  Up (0) is along the z-axis. CaWE: Measured counter-clockwise. Cafu engine: Measured clockwise.
574        // Heading/Yaw:  CaWE: North (0) is along the x-axis, measured counter-clockwise. Cafu engine: North is along the y-axis, measured clockwise.
575        // Bank/Roll  :  Up (0) is along the z-axis. CaWE: Measured counter-clockwise. Cafu engine: Measured clockwise.
576        Pitch=  -Pitch; while (Pitch<0.0) Pitch+=360.0; while (Pitch>360.0) Pitch-=360.0;
577        Yaw  =90-Yaw;   while (Yaw  <0.0) Yaw  +=360.0; while (Yaw  >360.0) Yaw  -=360.0;
578        Roll =  -Roll;  while (Roll <0.0) Roll +=360.0; while (Roll >360.0) Roll -=360.0;
579
580        Value=wxString::Format("%g %g %g", Pitch, Yaw, Roll);
581    }
582
583    if (MapFileVersion<=6 && Key=="origin")
584    {
585        const Vector3fT Origin=GetVector3f()/CAFU_ENG_SCALE;
586
587        Value=wxString::Format("%.1f %.1f %.1f", Origin.x, Origin.y, Origin.z);
588    }
589}
590
591
592void EntPropertyT::Save_cmap(std::ostream& OutFile) const
593{
594    // Don't save properties with default values into the file.
595    if (Key=="" || Value=="" || Value=="0") return;
596
597    wxString WriteValue=Value;
598
599    if (Key=="angles")
600    {
601        const Vector3fT Angles=GetVector3f();
602
603        float Pitch=Angles.x;
604        float Yaw  =Angles.y;
605        float Roll =Angles.z;
606
607        // Angles get special treatment because we measure angles a little differently:
608        // Pitch      :  Up (0) is along the z-axis. CaWE: Measured counter-clockwise. Cafu engine: Measured clockwise.
609        // Heading/Yaw:  CaWE: North (0) is along the x-axis, measured counter-clockwise. Cafu engine: North is along the y-axis, measured clockwise.
610        // Bank/Roll  :  Up (0) is along the z-axis. CaWE: Measured counter-clockwise. Cafu engine: Measured clockwise.
611        Pitch=  -Pitch; while (Pitch<0.0) Pitch+=360.0; while (Pitch>360.0) Pitch-=360.0;
612        Yaw  =90-Yaw;   while (Yaw  <0.0) Yaw  +=360.0; while (Yaw  >360.0) Yaw  -=360.0;
613        Roll =  -Roll;  while (Roll <0.0) Roll +=360.0; while (Roll >360.0) Roll -=360.0;
614
615        WriteValue=wxString::Format("%g %g %g", Pitch, Yaw, Roll);
616    }
617
618    // Keys may contain white-space, e.g. importing D3 map files may sometimes bring some with them.
619    if (Key.Find(' ')==-1) OutFile << "  "   << Key << " \""   << WriteValue << "\"\n";
620                      else OutFile << \"" << Key << "\" \"" << WriteValue << "\"\n";
621}
622
623
624void MapEntityBaseT::Load_cmap(TextParserT& TP, MapDocumentT& MapDoc, wxProgressDialog* ProgressDialog, unsigned long EntityNr)
625{
626    if (EntityNr==0)
627    {
628        MapFileVersion=0;
629
630        if (TP.PeekNextToken()=="Version")
631        {
632            TP.GetNextToken();
633            MapFileVersion=TP.GetNextTokenAsInt();
634        }
635
636        while (TP.PeekNextToken()=="GroupDef")
637        {
638            MapDoc.GetGroups().PushBack(new GroupT(GroupT::Create_cmap(TP)));
639        }
640    }
641
642    unsigned long NrOfPrimitives=0;
643
644    TP.AssertAndSkipToken("{");
645    MapElementT::Load_cmap(TP, MapDoc);
646
647    while (true)
648    {
649        std::string Token=TP.GetNextToken();
650        TP.PutBack(Token);
651
652        if (Token=="}")
653        {
654            // End of entity definition.
655            break;
656        }
657        else if (Token=="{")
658        {
659            // A brush definition.
660            AddPrim(MapBrushT::Create_cmap(TP, MapDoc, EntityNr, NrOfPrimitives));
661            NrOfPrimitives++;
662        }
663        else if (Token=="PatchDef")
664        {
665            // A patch definition.
666            MapBezierPatchT* BP=new MapBezierPatchT(MapDoc.GetGameConfig()->GetMatMan().GetDefaultMaterial(), MapDoc.GetLightMapMan());
667
668            BP->Load_cmap(TP, MapDoc);
669            AddPrim(BP);
670            NrOfPrimitives++;
671        }
672        else if (Token=="TerrainDef")
673        {
674            // A terrain definition.
675            MapTerrainT* Terrain=new MapTerrainT();
676
677            Terrain->Load_cmap(TP, MapDoc);
678            AddPrim(Terrain);
679            NrOfPrimitives++;
680        }
681        else if (Token=="PlantDef")
682        {
683            // A plant definition.
684            MapPlantT* Plant=new MapPlantT();
685
686            Plant->Load_cmap(TP, MapDoc);
687            AddPrim(Plant);
688            NrOfPrimitives++;
689        }
690        else if (Token=="ModelDef")
691        {
692            // A model definition.
693            MapModelT* Model=new MapModelT(MapDoc, "dummy", Vector3fT());
694
695            Model->Load_cmap(TP, MapDoc);
696            AddPrim(Model);
697            NrOfPrimitives++;
698        }
699        else
700        {
701            // A property definition.
702            EntPropertyT NewProp;
703            NewProp.Load_cmap(TP);
704
705            if (NewProp.Key=="wad")
706                continue;
707
708            if (NewProp.Key=="mapfile_version")
709            {
710                if (MapFileVersion<13) MapFileVersion=wxAtoi(NewProp.Value);
711
712                if (MapFileVersion<6 || MapFileVersion>MapDocumentT::CMAP_FILE_VERSION)
713                {
714                    wxMessageBox(wxString::Format("Expected cmap file version 6 to %u, but found version %u.", MapDocumentT::CMAP_FILE_VERSION, MapFileVersion), "Could not load cmap file. Sorry.");
715                    throw TextParserT::ParseError();
716                }
717                continue;
718            }
719
720            if (NewProp.Key=="angle")
721            {
722                // "angle" keys in cmap files?? This should never happen...
723                NewProp.Key="angles";
724
725                     if (NewProp.Value=="-1") NewProp.Value="-90 0 0";
726                else if (NewProp.Value=="-2") NewProp.Value="90 0 0";
727                else                          NewProp.Value="0 "+NewProp.Value+" 0";
728            }
729
730            // Insert the new property.
731            // We cannot simply call   m_Properties.PushBack(NewProp);   here, because there might already be a property with this key.
732            // In this case, instead of having another property with the same key, we want to keep the value of the last occurrence.
733            FindProperty(NewProp.Key, NULL, true /*Create*/)->Value=NewProp.Value;
734        }
735
736        if (ProgressDialog!=NULL && (NrOfPrimitives % 5)==0) ProgressDialog->Update(int(TP.GetReadPosPercent()*100.0));
737    }
738
739    TP.AssertAndSkipToken("}");
740
741
742    // "Post-process" the entity properties.
743    if (EntityNr>0)
744    {
745        int Index=-1;
746
747        // Set our origin from the "origin" property, then remove it from the properties list:
748        // the origin is a special-case that is not defined by the EntityClassDefs.lua scripts.
749        MapEntityT*   Ent =dynamic_cast<MapEntityT*>(this);
750        EntPropertyT* Prop=FindProperty("origin", &Index);
751        const bool    FoundOrigin=(Prop!=NULL);
752
753        if (Ent!=NULL && Prop!=NULL)
754        {
755            Ent->SetOrigin(Prop->GetVector3f());
756            m_Properties.RemoveAtAndKeepOrder(Index);
757        }
758
759/*      TODO: Turn this into a MapCheckDialogT issue!!
760        // Convert terrain entities to real terrains (map files >10 don't contain terrain entities anymore).
761        EntPropertyT* ClassName=Entity->FindProperty("classname");
762
763        if (MapFileVersion<11 && ClassName!=NULL && ClassName->Value=="Terrain")
764        {
765            MapTerrainT*  Terrain=new MapTerrainT();
766            EntPropertyT* HeightMap=Entity->FindProperty("heightmap_name");
767            EntPropertyT* Material=Entity->FindProperty("material_name");
768
769            if (HeightMap!=NULL) Terrain->LoadHeightData(m_MapDoc.GetGameConfig()->ModDir+"/"+HeightMap->Value);
770            if (Material!=NULL) Terrain->SetMaterial(m_MapDoc.GetGameConfig()->GetMatMan().FindMaterial(Material->Value, true));
771
772            Terrain->SetTerrainBounds(Entity->GetBB()); // Reset the terrains bounds to those of the entity.
773            AddPrim(Terrain);
774        }
775*/
776
777        // Set our class from the "classname" property, and remove it as well:
778        // just like the "origin" property, it is a special case wrt. the EntityClassDefs.lua scripts.
779        Prop=FindProperty("classname", &Index);
780
781        if (Prop!=NULL && Prop->Value!="")
782        {
783            const wxString      ClassName  =Prop->Value;
784            const EntityClassT* EntityClass=MapDoc.GetGameConfig()->FindClass(ClassName);
785
786            SetClass(EntityClass!=NULL ? EntityClass : MapDoc.FindOrCreateUnknownClass(ClassName, FoundOrigin));
787        }
788        else
789        {
790            SetClass(MapDoc.FindOrCreateUnknownClass("undefined", FoundOrigin));
791        }
792    }
793
794    // Remove our "classname" property, no matter which value it has.
795    // For worlds, we've set our entity class to "worldspawn" in the constructor already, the map file cannot override this.
796    // For entities, the proper class has been set above.
797    int Index;
798    if (FindProperty("classname", &Index)!=NULL)
799        m_Properties.RemoveAtAndKeepOrder(Index);
800}
801
802
803void MapEntityBaseT::Save_cmap(const MapDocumentT& MapDoc, std::ostream& OutFile, unsigned long EntityNr, const BoundingBox3fT* Intersecting) const
804{
805    // If it's a solid custom entity but doesn't have any primitives, don't save it.
806    if (EntityNr>0 && m_Class->IsSolidClass() && m_Primitives.Size()==0)
807    {
808        OutFile << "\n// Solid entity " << EntityNr << " of class " << m_Class->GetName() << " has no primitives - not saved.\n";
809        return;
810    }
811
812    OutFile << "\n"
813            << "{"; if (WriteComments) OutFile << " // Entity " << EntityNr; OutFile << "\n";
814
815    // Save the groups info.
816    MapElementT::Save_cmap(OutFile, EntityNr, MapDoc);
817
818    // Save the properties.
819    if (FindProperty("classname")==NULL)
820    {
821        EntPropertyT("classname", m_Class->GetName()).Save_cmap(OutFile);
822    }
823
824    for (unsigned long PropNr=0; PropNr<m_Properties.Size(); PropNr++)
825        m_Properties[PropNr].Save_cmap(OutFile);
826
827    if (EntityNr>0 && !m_Class->IsSolidClass())
828    {
829        const MapEntityT* Ent=dynamic_cast<const MapEntityT*>(this);
830
831        // Note that convertToString(m_Origin) returns not "x y z" but "(x, y, z)".
832        EntPropertyT("origin", serialize(Ent->GetOrigin())).Save_cmap(OutFile);
833    }
834
835    // Save the primitives.
836    for (unsigned long PrimNr=0; PrimNr<m_Primitives.Size(); PrimNr++)
837    {
838        const MapPrimitiveT* Prim=m_Primitives[PrimNr];
839
840        if (!Intersecting || Prim->GetBB().Intersects(*Intersecting))
841        {
842            Prim->Save_cmap(OutFile, PrimNr, MapDoc);
843        }
844    }
845
846    OutFile << "}\n";
847}
Note: See TracBrowser for help on using the browser.