root/cafu/trunk/CaBSP/LoadWorld.cpp

Revision 455, 31.2 KB (checked in by Carsten, 4 months ago)

Updated copyright banners (in C++ files).

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/******************/
23/*** Load World ***/
24/******************/
25
26#include <iostream>
27#include "ClipSys/CollisionModel_static.hpp"
28#include "ConsoleCommands/ConsoleWarningsOnly.hpp"
29#include "TextParser/TextParser.hpp"
30#include "SceneGraph/BezierPatchNode.hpp"
31#include "SceneGraph/BspTreeNode.hpp"
32#include "SceneGraph/FaceNode.hpp"
33#include "SceneGraph/TerrainNode.hpp"
34#include "SceneGraph/PlantNode.hpp"
35#include "SceneGraph/ModelNode.hpp"
36#include "MapFile.hpp"
37#include "Models/ModelManager.hpp"
38#include "Plants/PlantDescrMan.hpp"
39
40
41using namespace cf;
42
43
44void MapFileSanityCheck(const ArrayT<MapFileEntityT>& MFEntityList)
45{
46    unsigned long NrOfWorldSpawnEntities     =0;
47    unsigned long NrOfInfoPlayerStartEntities=0;
48
49    for (unsigned long EntityNr=0; EntityNr<MFEntityList.Size(); EntityNr++)
50    {
51        const MapFileEntityT& E=MFEntityList[EntityNr];
52        const std::map<std::string, std::string>::const_iterator It=E.MFProperties.find("classname");
53
54        if (It==E.MFProperties.end()) Error("\"classname\" property not found in entity %lu.", EntityNr);
55
56        // If we found the 'classname worldspawn' property pair, we count the occurence, because there must only be exactly one such entity.
57        // Additionally, our map file spec. implies that there MUST be a 'mapfile_version XX' pair following the 'classname worldspawn' pair.
58        if (It->second=="worldspawn")
59        {
60            if (EntityNr!=0) Error("Entity %u is the 'worldspawn' entity, but entity 0 is expected to be the only 'worldspawn' entity.", EntityNr);
61            if (E.MFProperties.size()==1) Error("Entity %u is the worldspawn entity, but has only one PPair (missing mapfile_version).", EntityNr);
62
63            // These two issues are directly checked while loading.
64            // The pair also must not be precisely at second position (index 1) anymore.
65            // if (E.MFPropPairs[1].Key!="mapfile_version") Error("Entity %u: Second PPair key is not 'mapfile_version'.", EntityNr);
66            // if (atoi(E.MFPropPairs[1].Value.c_str())!=9)) Error("Bad map file version: Expected %s, got %s.", "9", E.MFPropPairs[1].Value.c_str());
67
68            if (E.MFBrushes.Size()<4)
69            {
70                Console->Print(cf::va("This map only has %lu brush%s, but CSG requires at least 4 (better 6).\n", E.MFBrushes.Size(), E.MFBrushes.Size()==1 ? "" : "es"));
71                Console->Print("That means that the minimum of geometry you have to provide is a small room,\n");
72                Console->Print("consisting of 4 walls, the floor, and the ceiling.\n");
73                Console->Print("That in turn means that you need at least 6 brushes to create a \"closed room\".\n");
74                Console->Print("(The minimum is 4 brushes for a \"closed pyramid\".  :-)\n");
75                Error("Too few brushes.");
76            }
77
78            NrOfWorldSpawnEntities++;
79        }
80
81        // If this is a 'classname info_player_start' entity, we count the occurence, because we insist that there is at least one such entity.
82        if (It->second=="info_player_start")
83            NrOfInfoPlayerStartEntities++;
84
85        // No further checks should be necessary for now. We can always ignore invalid property pairs and/or fill in default values.
86    }
87
88    if (NrOfWorldSpawnEntities     !=1) Error("Found %u worldspawn entities, expected exactly 1.", NrOfWorldSpawnEntities);
89    if (NrOfInfoPlayerStartEntities==0) Error("Found no info_player_start entities at all, expected at least 1.", NrOfInfoPlayerStartEntities);
90}
91
92
93// Nimmt ein Token, das aus drei durch Leerzeichen voneinander getrennten Zahlen besteht und gibt sie als VectorT zurück.
94VectorT GetVectorFromTripleToken(const std::string& TripleToken)
95{
96    VectorT V;
97    std::istringstream iss(TripleToken);
98
99    iss >> V.x >> V.y >> V.z;   // Bank, Heading and Pitch.
100
101    return V;
102}
103
104
105/* OBSOLETE - CaWE saves the cmap files now immediately right.
106// Nimmt ein Token, das aus drei durch Leerzeichen voneinander getrennten Zahlen besteht, die als Winkel interpretiert werden,
107// und gibt die dazugehörige Richtung als VectorT zurück.
108VectorT GetDirFromTripleAngleToken(const char* TripleToken)
109{
110    VectorT V;
111    std::istringstream iss(TripleToken);
112
113    iss >> V.x >> V.y >> V.z;
114
115    VectorT D(0.0, 1.0, 0.0);
116
117    // Sigh. All this angle related stuff *REALLY* must be checked:
118    // Which angles rotates about which axis, and in what order?
119    // Also wrt. the BBs of static detail models!
120    D=D.GetRotX(V.x);
121    D=D.GetRotZ(V.y);
122
123    return normalize(D, 0.0);
124} */
125
126
127// Computes the brush polygons from a MapFileBrushT.
128void ComputeBrushPolys(const MapFileBrushT& MFBrush, ArrayT< Polygon3T<double> >& BrushPolys, const unsigned long EntityNr, const unsigned long BrushNr)
129{
130    // Konstruiere die Polygone des Brushes.
131    for (unsigned long MFPlaneNr=0; MFPlaneNr<MFBrush.MFPlanes.Size(); MFPlaneNr++)
132    {
133        BrushPolys.PushBackEmpty();
134        BrushPolys[BrushPolys.Size()-1].Plane=MFBrush.MFPlanes[MFPlaneNr].Plane;
135    }
136
137
138    Polygon3T<double>::Complete(BrushPolys, MapT::RoundEpsilon);
139
140
141    // Prüfe die Gültigkeit der konstruierten Polygone.
142    // Eine explizite Gültigkeitsprüfung ist sinnvoll und notwendig um sicherzustellen, daß wir mit "sauberen" Eingabedaten anfangen!
143    for (unsigned long MFPlaneNr=0; MFPlaneNr<BrushPolys.Size(); MFPlaneNr++)
144        if (!BrushPolys[MFPlaneNr].IsValid(MapT::RoundEpsilon, MapT::MinVertexDist))
145        {
146            Console->Print("\n");
147            for (unsigned long PlaneNr=0; PlaneNr<MFBrush.MFPlanes.Size(); PlaneNr++)
148                std::cout << convertToString(MFBrush.MFPlanes[PlaneNr].Plane) << " "
149                          << MFBrush.MFPlanes[PlaneNr].Material->Name << "\n";
150
151            Console->Print("\n");
152            for (unsigned long PlaneNr=0; PlaneNr<BrushPolys.Size(); PlaneNr++)
153            {
154                std::cout << "Plane " << convertToString(BrushPolys[PlaneNr].Plane) << ", Vertices ";
155
156                for (unsigned long VertexNr=0; VertexNr<BrushPolys[PlaneNr].Vertices.Size(); VertexNr++)
157                    std::cout << convertToString(BrushPolys[PlaneNr].Vertices[VertexNr]) << " ";
158
159                std::cout << "\n";
160            }
161
162            Error("Entity #%u, brush #%u: polygon #%u is invalid.", EntityNr, BrushNr, MFPlaneNr);
163        }
164}
165
166
167void ComputeBrushFaces(const MapFileBrushT& MFBrush, WorldT& World, cf::SceneGraph::BspTreeNodeT* BspTree,
168                       ArrayT<VectorT>& DrawWorldOutsidePointSamples, const unsigned long EntityNr, const unsigned long BrushNr)
169{
170    // Compute the overall clip flags (combined from the faces) of the brush.
171    unsigned long CombinedClipFlags_Ored =0;
172    unsigned long CombinedClipFlags_Anded=0xFFFFFFFF;
173
174    for (unsigned long MFPlaneNr=0; MFPlaneNr<MFBrush.MFPlanes.Size(); MFPlaneNr++)
175    {
176        const MapFilePlaneT& MFPlane=MFBrush.MFPlanes[MFPlaneNr];
177
178        CombinedClipFlags_Ored |=MFPlane.Material->ClipFlags;
179        CombinedClipFlags_Anded&=MFPlane.Material->ClipFlags;
180
181        // Print a warning if a materials stops portals, but not players and monsters.
182        if ((MFPlane.Material->ClipFlags & MaterialT::Clip_BspPortals)!=0 &&
183            !((MFPlane.Material->ClipFlags & MaterialT::Clip_Players)!=0 && (MFPlane.Material->ClipFlags & MaterialT::Clip_Monsters)!=0))
184            Console->Warning("Material "+MFPlane.Material->Name+" stops portals, but not players and monsters!\n");
185    }
186
187    // The materials of all faces should have the same value for the MaterialT::Clip_BspPortals flag - all on or all off.
188    // Having mixed flags is not really a problem, but can lead to some "unusual" results later during Portalization,
189    // e.g. when a cube that is floating in mid-air has 5 sides with and one side without the bspPortals flag set.
190    if ((CombinedClipFlags_Ored & MaterialT::Clip_BspPortals)!=(CombinedClipFlags_Anded & MaterialT::Clip_BspPortals))
191        Console->Warning(cf::va("Entity %lu, brush %lu: Faces have materials with mismatching \"bspPortal\" clip flags.\n", EntityNr, BrushNr));
192
193
194    // Compute the brush polygons.
195    ArrayT< Polygon3T<double> > BrushPolys;
196    ComputeBrushPolys(MFBrush, BrushPolys, EntityNr, BrushNr);
197
198
199    // Compute the center of the brush, to be used for leak detection later.
200    // The algorithm is probably not 100% correct, in the sense that the computed center
201    // might be different from mathematically correct center, but that's negligible.
202    VectorT       BrushCenter;
203    unsigned long AverageCount=0;
204
205    for (unsigned long MFPlaneNr=0; MFPlaneNr<BrushPolys.Size(); MFPlaneNr++)
206        for (unsigned long VertexNr=0; VertexNr<BrushPolys[MFPlaneNr].Vertices.Size(); VertexNr++)
207        {
208            BrushCenter+=BrushPolys[MFPlaneNr].Vertices[VertexNr];
209            AverageCount++;
210        }
211
212    BrushCenter=scale(BrushCenter, 1.0/double(AverageCount));
213
214    // Include a small sanity check to make sure that the 'BrushCenter' is really inside the brush (think of rounding errors...).
215    for (unsigned long MFPlaneNr=0; MFPlaneNr<BrushPolys.Size(); MFPlaneNr++)
216        if (BrushPolys[MFPlaneNr].Plane.GetDistance(BrushCenter) > -MapT::RoundEpsilon)
217            Error("Entity #%u, brush #%u: BrushCenter is outside brush.", EntityNr, BrushNr);
218
219    // If all faces of the brush are solid for BSP portals (unlike e.g. glass, there cannot be a portal that sees into the brush),
220    // consider the inside of the brush as "solid" and "outside of the visible world", and thus collect an outside point sample.
221    if (CombinedClipFlags_Anded & MaterialT::Clip_BspPortals) DrawWorldOutsidePointSamples.PushBack(BrushCenter);
222
223
224    for (unsigned long MFPlaneNr=0; MFPlaneNr<MFBrush.MFPlanes.Size(); MFPlaneNr++)
225    {
226        const MapFilePlaneT& MFPlane=MFBrush.MFPlanes[MFPlaneNr];
227
228        if (((MFPlane.Material->AmbientShaderName=="none" && MFPlane.Material->LightShaderName=="none") || MFPlane.Material->NoDraw) &&
229            (MFPlane.Material->ClipFlags & MaterialT::Clip_BspPortals)==0)
230        {
231            // A face enters the draw BSP tree only if it is visible (like walls, glass, water) or stops the BSP flood-fill
232            // (like invisible "caulk" materials), or both (like most of the normal walls).
233            // Faces with materials that don't draw at all and are not "caulk" (invisible materials that nontheless stop portals)
234            // can be omitted from the BSP tree. As the BSP tree is only used for drawing, this prevents unnecessary leaves and thus complexity.
235            continue;
236        }
237
238        cf::SceneGraph::FaceNodeT::TexInfoT TI;
239
240        TI.U      =MFPlane.U.AsVectorOfFloat();
241        TI.V      =MFPlane.V.AsVectorOfFloat();
242        TI.OffsetU=float(MFPlane.ShiftU);
243        TI.OffsetV=float(MFPlane.ShiftV);
244
245        BspTree->FaceChildren.PushBack(new cf::SceneGraph::FaceNodeT(World.LightMapMan, World.SHLMapMan, BrushPolys[MFPlaneNr], MFPlane.Material, TI));
246    }
247}
248
249
250// Ließt ein MapFile, das die der Version entsprechenden "MapFile Specifications" erfüllen muß, in die World ein.
251// Dabei werden folgende Komponenten der World modifiziert (ausgefüllt, u.U. nur teilweise):
252// Map.Faces, Map.TexInfos, Map.PointLights, InfoPlayerStarts und GameEntities.
253void LoadWorld(const char* LoadName, const std::string& GameDirectory, ModelManagerT& ModelMan, WorldT& World, ArrayT<VectorT>& DrawWorldOutsidePointSamples)
254{
255    World.PlantDescrMan.SetModDir(GameDirectory);
256
257    Console->Print(cf::va("\n*** Load World %s ***\n", LoadName));
258
259
260    // Parse all map entities into the MFEntityList.
261    ArrayT<MapFileEntityT> MFEntityList;
262    TextParserT            TP(LoadName, "({})");
263
264    try
265    {
266        MapFileReadHeader(TP);
267
268        while (!TP.IsAtEOF())
269        {
270            MFEntityList.PushBack(MapFileEntityT(MFEntityList.Size(), TP));
271        }
272    }
273    catch (const TextParserT::ParseError&)
274    {
275        Error("Problem with parsing the map near byte %lu (%.3f%%) of the file.", TP.GetReadPosByte(), TP.GetReadPosPercent()*100.0);
276    }
277
278
279    // Perform certain sanity checks on the map (MFEntityList), look into the function for details.
280    MapFileSanityCheck(MFEntityList);
281
282
283    // 'func_group' entities are just for editor convenience, thus toss all their brushes into the 'worldspawn' entity.
284    // 'func_wall' and 'func_water' entities exist for historic reasons (render walls specially, treat water specially),
285    //     but are largely obsolete now with the Material System and Clip System and should probably be removed from
286    //     the maps and the EntityClassDefs.lua files.
287    // Assumptions:
288    // 1.) Entity 0 is the 'worldspawn' entity.
289    // 2.) Each entity has the "classname" property.
290    for (unsigned long EntityNr=1; EntityNr<MFEntityList.Size(); EntityNr++)
291    {
292        const std::string EntClassName=MFEntityList[EntityNr].MFProperties["classname"];
293
294        if (EntClassName=="func_group" || EntClassName=="func_wall" || EntClassName=="func_water")
295        {
296            // Copy all brushes of this entity into the 'worldspawn' entity.
297            for (unsigned long BrushNr=0; BrushNr<MFEntityList[EntityNr].MFBrushes.Size(); BrushNr++)
298                MFEntityList[0].MFBrushes.PushBack(MFEntityList[EntityNr].MFBrushes[BrushNr]);
299
300            // Copy all bezier patches of this entity into the 'worldspawn' entity.
301            for (unsigned long BPNr=0; BPNr<MFEntityList[EntityNr].MFPatches.Size(); BPNr++)
302                MFEntityList[0].MFPatches.PushBack(MFEntityList[EntityNr].MFPatches[BPNr]);
303
304            // Delete this entity.
305            MFEntityList[EntityNr]=MFEntityList[MFEntityList.Size()-1];
306            MFEntityList.DeleteBack();
307            EntityNr--;
308        }
309    }
310
311
312    // *** HACK HACK HACK HACK ***   (In fact, two hacks total.)
313    //
314    // In TechDemo.cmap, "light" entities sometimes have Bezier Patches (and brushes?).
315    // However, Cafu can currently not render them as such, thus let's move them into the "worldspawn" entity.
316    for (unsigned long EntityNr=1; EntityNr<MFEntityList.Size(); EntityNr++)
317    {
318        MapFileEntityT& E=MFEntityList[EntityNr];
319
320        if (E.MFProperties["classname"]=="light")
321        {
322            // Move all brushes of this entity into the 'worldspawn' entity.
323            for (unsigned long BrushNr=0; BrushNr<E.MFBrushes.Size(); BrushNr++)
324                MFEntityList[0].MFBrushes.PushBack(E.MFBrushes[BrushNr]);
325            E.MFBrushes.Clear();
326
327            // Move all bezier patches of this entity into the 'worldspawn' entity.
328            for (unsigned long BPNr=0; BPNr<E.MFPatches.Size(); BPNr++)
329                MFEntityList[0].MFPatches.PushBack(E.MFPatches[BPNr]);
330            E.MFPatches.Clear();
331
332#if 1
333            // Delete this entity.
334            MFEntityList[EntityNr]=MFEntityList[MFEntityList.Size()-1];
335            MFEntityList.DeleteBack();
336            EntityNr--;
337#else
338            // Now the 2nd part of the hack: Convert the "light" entity into a "PointLightSource" entity,
339            // it will be included with the other regular game entities below.
340            // NOTE: "PointLight" and "PointLightSource" are NOT THE SAME!
341            bool HaveColor =false;
342            bool HaveRadius=false;
343
344            for (unsigned long PPairNr=1; PPairNr<E.MFPropPairs.Size(); PPairNr++)
345                     if (E.MFPropPairs[PPairNr].Key=="_color") HaveColor=true;
346                else if (E.MFPropPairs[PPairNr].Key=="light_radius") HaveRadius=true;
347
348            if (!HaveColor || !HaveRadius) continue;
349
350            // Okay, it's a point light source.
351            E.MFPropPairs[0].Value="PointLightSource";
352
353            for (unsigned long PPairNr=1; PPairNr<E.MFPropPairs.Size(); PPairNr++)
354            {
355                if (E.MFPropPairs[PPairNr].Key=="_color")
356                {
357                    const Vector3dT LightColor=GetVectorFromTripleToken(E.MFPropPairs[PPairNr].Value);
358                    char            str[256];
359
360                    sprintf(str, "%i %i %i", int(LightColor.x*255), int(LightColor.y*255), int(LightColor.z*255));
361
362                    E.MFPropPairs[PPairNr].Key  ="light_color_diff";
363                    E.MFPropPairs[PPairNr].Value=str;
364
365                    MapFilePropPairT SpecCol;
366                    SpecCol.Key  ="light_color_spec";
367                    SpecCol.Value=str;
368                    E.MFPropPairs.PushBack(SpecCol);
369                }
370                else if (E.MFPropPairs[PPairNr].Key=="light_radius")
371                {
372                    const Vector3dT LightRadius=GetVectorFromTripleToken(E.MFPropPairs[PPairNr].Value);
373                    const double    LightRAvg  =(LightRadius.x+LightRadius.y+LightRadius.z)/3.0 * CA3DE_SCALE;
374                    char            str[256];
375
376                    sprintf(str, "%i", int(LightRAvg));
377
378                    E.MFPropPairs[PPairNr].Value=str;
379                }
380                else if (E.MFPropPairs[PPairNr].Key=="light_origin")
381                {
382                    // Update our "origin" with this value??
383                    // E.MFPropPairs[OriginPPairNr].Value=E.MFPropPairs[PPairNr].Value;
384                }
385                else if (E.MFPropPairs[PPairNr].Key=="light_target" || E.MFPropPairs[PPairNr].Key=="light_up" || E.MFPropPairs[PPairNr].Key=="light_right")
386                {
387                    Console->Warning("\"light\" entity %lu, mixed point and projected light keys?\n", EntityNr);
388                }
389            }
390#endif
391        }
392    }
393
394
395    // Finally, fill-in our World data structures!
396    for (unsigned long EntityNr=0; EntityNr<MFEntityList.Size(); EntityNr++)
397    {
398        const MapFileEntityT& E=MFEntityList[EntityNr];
399        const std::map<std::string, std::string>::const_iterator ClassNamePair=E.MFProperties.find("classname");
400
401        if (ClassNamePair==E.MFProperties.end()) Error("\"classname\" property not found in entity %lu.", EntityNr);
402
403        if (ClassNamePair->second=="worldspawn")
404        {
405            std::map<std::string, std::string>::const_iterator It;
406
407            It=E.MFProperties.find("lightmap_patchsize");
408            if (It!=E.MFProperties.end())
409            {
410                cf::SceneGraph::FaceNodeT::LightMapInfoT::PatchSize=float(atof(It->second.c_str()));
411
412                if (cf::SceneGraph::FaceNodeT::LightMapInfoT::PatchSize<  50.0) { cf::SceneGraph::FaceNodeT::LightMapInfoT::PatchSize=  50.0; Console->Print("NOTE: LightMap PatchSize clamped to 50.\n"  ); }
413                if (cf::SceneGraph::FaceNodeT::LightMapInfoT::PatchSize>2000.0) { cf::SceneGraph::FaceNodeT::LightMapInfoT::PatchSize=2000.0; Console->Print("NOTE: LightMap PatchSize clamped to 2000.\n"); }
414            }
415
416            It=E.MFProperties.find("shlmap_patchsize");
417            if (It!=E.MFProperties.end())
418            {
419                cf::SceneGraph::FaceNodeT::SHLMapInfoT::PatchSize=float(atof(It->second.c_str()));
420
421                if (cf::SceneGraph::FaceNodeT::SHLMapInfoT::PatchSize<  50.0) { cf::SceneGraph::FaceNodeT::SHLMapInfoT::PatchSize=  50.0; Console->Print("NOTE: SHLMap PatchSize clamped to 50.\n"  ); }
422                if (cf::SceneGraph::FaceNodeT::SHLMapInfoT::PatchSize>2000.0) { cf::SceneGraph::FaceNodeT::SHLMapInfoT::PatchSize=2000.0; Console->Print("NOTE: SHLMap PatchSize clamped to 2000.\n"); }
423            }
424
425
426            for (unsigned long BrushNr=0; BrushNr<E.MFBrushes.Size(); BrushNr++)
427                ComputeBrushFaces(E.MFBrushes[BrushNr], World, World.BspTree, DrawWorldOutsidePointSamples, EntityNr, BrushNr);
428
429            for (unsigned long BPNr=0; BPNr<E.MFPatches.Size(); BPNr++)
430            {
431                const MapFileBezierPatchT& BP=E.MFPatches[BPNr];
432
433                World.BspTree->OtherChildren.PushBack(new cf::SceneGraph::BezierPatchNodeT(World.LightMapMan, BP.SizeX, BP.SizeY, BP.ControlPoints, BP.SubdivsHorz, BP.SubdivsVert, BP.Material));
434            }
435
436            for (unsigned long TerrainNr=0; TerrainNr<E.MFTerrains.Size(); TerrainNr++)
437            {
438                const MapFileTerrainT& Terrain=E.MFTerrains[TerrainNr];
439
440                World.Terrains.PushBack(new SharedTerrainT(Terrain.Bounds, Terrain.SideLength, Terrain.HeightData, Terrain.Material));
441                World.BspTree->OtherChildren.PushBack(new cf::SceneGraph::TerrainNodeT(Terrain.Bounds, World.Terrains[TerrainNr]->Terrain, TerrainNr, Terrain.Material->Name));
442            }
443
444            for (unsigned long PlantNr=0; PlantNr<E.MFPlants.Size(); PlantNr++)
445            {
446                const MapFilePlantT& Plant=E.MFPlants[PlantNr];
447
448                World.BspTree->OtherChildren.PushBack(new cf::SceneGraph::PlantNodeT(World.PlantDescrMan.GetPlantDescription(Plant.DescrFileName) , Plant.RandomSeed, Plant.Position, Plant.Angles));
449            }
450
451            for (unsigned long ModelNr=0; ModelNr<E.MFModels.Size(); ModelNr++)
452            {
453                std::string          ErrorMsg;
454                const MapFileModelT& Model=E.MFModels[ModelNr];
455                const CafuModelT*    CafuM=ModelMan.GetModel(GameDirectory+"/"+Model.Model, &ErrorMsg);
456
457                if (ErrorMsg!="") Console->Warning(ErrorMsg);
458                World.BspTree->OtherChildren.PushBack(new cf::SceneGraph::ModelNodeT(CafuM, Model.Label, Model.Origin, Model.Angles, Model.Scale, Model.SeqNumber, Model.FrameOffset, Model.FrameTimeScale, Model.Animate));
459            }
460        }
461        else if (ClassNamePair->second=="PointLight")
462        {
463            const double Pi=3.14159265358979323846;
464
465            PointLightT PL;
466            std::map<std::string, std::string>::const_iterator It;
467
468            PL.Dir  =VectorT(0.0, 1.0, 0.0);
469            PL.Angle=float(Pi);
470
471            It=E.MFProperties.find("origin"       ); if (It!=E.MFProperties.end()) PL.Origin     =GetVectorFromTripleToken(It->second)*CA3DE_SCALE;
472            It=E.MFProperties.find("angles"       ); if (It!=E.MFProperties.end()) PL.Dir        =Vector3dT(0, 0, -1);   // Sigh... yet another hack! :-/   // GetVectorFromTripleToken(It->second);
473            It=E.MFProperties.find("opening_angle"); if (It!=E.MFProperties.end()) PL.Angle      =float(atof(It->second.c_str())/180.0*Pi);
474            It=E.MFProperties.find("intensity_r"  ); if (It!=E.MFProperties.end()) PL.Intensity.x=atof(It->second.c_str());
475            It=E.MFProperties.find("intensity_g"  ); if (It!=E.MFProperties.end()) PL.Intensity.y=atof(It->second.c_str());
476            It=E.MFProperties.find("intensity_b"  ); if (It!=E.MFProperties.end()) PL.Intensity.z=atof(It->second.c_str());
477
478            if (PL.Angle<0.0) PL.Angle=0.0;
479            if (PL.Angle> Pi) PL.Angle=float(Pi);
480
481            World.PointLights.PushBack(PL);
482        }
483     /* else if (ClassNamePair->second=="func_wall")    // Special case, handled above.
484        {
485        }
486        else if (ClassNamePair->second=="func_water")   // Special case, handled above.
487        {
488        } */
489        else if (ClassNamePair->second=="info_player_start")
490        {
491            InfoPlayerStartT IPS;
492            std::map<std::string, std::string>::const_iterator It;
493
494            IPS.Heading=0;
495            IPS.Pitch  =0;
496            IPS.Bank   =0;
497
498            It=E.MFProperties.find("origin"); if (It!=E.MFProperties.end()) IPS.Origin =GetVectorFromTripleToken(It->second.c_str())*CA3DE_SCALE;
499            It=E.MFProperties.find("angles"); if (It!=E.MFProperties.end()) IPS.Heading=(unsigned short)(GetVectorFromTripleToken(It->second).y*8192.0/45.0);
500
501            World.InfoPlayerStarts.PushBack(IPS);
502        }
503        else
504        {
505            // Console->Print("INFO: PROCESSING GAME ENTITY "+cf::va("%lu", World.GameEntities.Size())+" (class \""+ClassNamePair->second+"\"):\n");
506            // Console->Print("***********************************************************************************\n\n");
507
508            // Es ist ein Game/MOD/DLL-Entity, kein Engine-Entity!
509            GameEntityT* GE=new GameEntityT(E.MFIndex);
510
511            // 1. Copy the properties.
512            GE->Properties=E.MFProperties;
513
514            // 2. Copy the origin point.
515            std::map<std::string, std::string>::const_iterator It=E.MFProperties.find("origin");
516            if (It!=E.MFProperties.end())
517            {
518                // Der Origin ist etwas besonderes, denn er kommt bei allen "point entities" vor
519                // (bei "solid entities" idR in Form eines Origin-Brushes),
520                // und in der Engine (EntityStateT) sogar bei allen Entities.
521                GE->Origin=GetVectorFromTripleToken(It->second)*CA3DE_SCALE;
522            }
523
524            // 3. Fill-in the Terrains array.
525            for (unsigned long TerrainNr=0; TerrainNr<E.MFTerrains.Size(); TerrainNr++)
526            {
527                const MapFileTerrainT& Terrain=E.MFTerrains[TerrainNr];
528
529                GE->Terrains.PushBack(new SharedTerrainT(Terrain.Bounds, Terrain.SideLength, Terrain.HeightData, Terrain.Material));
530            }
531
532            // 4. Create a new BSP tree.
533            GE->BspTree=new cf::SceneGraph::BspTreeNodeT;
534
535            // 5. Build the collision model (if this entity has one that is made of map primitives).
536            if (E.MFBrushes.Size()>0 || E.MFPatches.Size()>0 || GE->Terrains.Size()>0)
537            {
538                ArrayT<cf::ClipSys::CollisionModelStaticT::TerrainRefT> ShTe;
539
540                for (unsigned long TerrainNr=0; TerrainNr<GE->Terrains.Size(); TerrainNr++)
541                {
542                    const SharedTerrainT* ST=GE->Terrains[TerrainNr];
543
544                    ShTe.PushBack(cf::ClipSys::CollisionModelStaticT::TerrainRefT(&ST->Terrain, ST->Material, ST->BB));
545                }
546
547                GE->CollModel=new cf::ClipSys::CollisionModelStaticT(E, ShTe, true /*Use generic brushes.*/);
548            }
549
550            // 6. Collect the geometry primitives for the BSP tree.
551            ArrayT<VectorT> GameEntityOutsidePointSamples;
552
553            for (unsigned long BrushNr=0; BrushNr<E.MFBrushes.Size(); BrushNr++)
554            {
555                ComputeBrushFaces(E.MFBrushes[BrushNr], World, GE->BspTree, GameEntityOutsidePointSamples, EntityNr, BrushNr);
556            }
557
558            for (unsigned long BPNr=0; BPNr<E.MFPatches.Size(); BPNr++)
559            {
560                const MapFileBezierPatchT& BP=E.MFPatches[BPNr];
561
562                GE->BspTree->OtherChildren.PushBack(new cf::SceneGraph::BezierPatchNodeT(World.LightMapMan, BP.SizeX, BP.SizeY, BP.ControlPoints, BP.SubdivsHorz, BP.SubdivsVert, BP.Material));
563            }
564
565            for (unsigned long TerrainNr=0; TerrainNr<E.MFTerrains.Size(); TerrainNr++)
566            {
567                const MapFileTerrainT& Terrain=E.MFTerrains[TerrainNr];
568
569                // This has already done been done above: GE->Terrains.PushBack(new SharedTerrainT(...));
570                GE->BspTree->OtherChildren.PushBack(new cf::SceneGraph::TerrainNodeT(Terrain.Bounds, GE->Terrains[TerrainNr]->Terrain, TerrainNr, Terrain.Material->Name));
571            }
572
573            for (unsigned long PlantNr=0; PlantNr<E.MFPlants.Size(); PlantNr++)
574            {
575                const MapFilePlantT& Plant=E.MFPlants[PlantNr];
576
577                GE->BspTree->OtherChildren.PushBack(new cf::SceneGraph::PlantNodeT(World.PlantDescrMan.GetPlantDescription(Plant.DescrFileName) , Plant.RandomSeed, Plant.Position, Plant.Angles));
578            }
579
580            for (unsigned long ModelNr=0; ModelNr<E.MFModels.Size(); ModelNr++)
581            {
582                std::string          ErrorMsg;
583                const MapFileModelT& Model=E.MFModels[ModelNr];
584                const CafuModelT*    CafuM=ModelMan.GetModel(GameDirectory+"/"+Model.Model, &ErrorMsg);
585
586                if (ErrorMsg!="") Console->Warning(ErrorMsg);
587                GE->BspTree->OtherChildren.PushBack(new cf::SceneGraph::ModelNodeT(CafuM, Model.Label, Model.Origin, Model.Angles, Model.Scale, Model.SeqNumber, Model.FrameOffset, Model.FrameTimeScale, Model.Animate));
588            }
589
590
591            // 7. Compute the BSP tree.
592            // It's done immediately here rather than after the map has been loaded completely
593            // so that we don't have to keep the OutsidePointSamples array.
594            BspTreeBuilderT BspTreeBuilder(GE->BspTree, false /*most simple tree*/, false /*min face splits*/);
595
596            ArrayT<Vector3dT> EmptyFloodFillSources;
597            std::string       EmptyMapFileName="";  // Entity BSP trees aren't flood-filled, so they cannot leak, so we never need to write a .pts point file for them.
598
599            // Temporarily filter the console output by redirecting everything through the warnings-only console.
600            cf::ConsoleWarningsOnlyT ConWarnOnly(Console);
601            cf::ConsoleI* PrevConsole=Console;
602            Console=&ConWarnOnly;
603
604            BspTreeBuilder.Build(ClassNamePair->second=="worldspawn", EmptyFloodFillSources, GameEntityOutsidePointSamples, EmptyMapFileName);
605
606            // Restore the previous console.
607            Console=PrevConsole;
608
609            // 8. Add the entity to the worlds list.
610            World.GameEntities.PushBack(GE);
611        }
612    }
613
614
615    Console->Print("All game entities done, processing the worldspawn entity now.\n");
616    Console->Print(std::string("Preparing worldspawn clip data (")+GetTimeSinceProgramStart()+")...");
617
618    ArrayT<cf::ClipSys::CollisionModelStaticT::TerrainRefT> ShTe;
619
620    for (unsigned long TerrainNr=0; TerrainNr<World.Terrains.Size(); TerrainNr++)
621    {
622        const SharedTerrainT* ST=World.Terrains[TerrainNr];
623
624        ShTe.PushBack(cf::ClipSys::CollisionModelStaticT::TerrainRefT(&ST->Terrain, ST->Material, ST->BB));
625    }
626
627    World.CollModel=new cf::ClipSys::CollisionModelStaticT(MFEntityList[0], ShTe, false /*Use brushes with precomputed bevel planes.*/);
628    Console->Print(std::string(" done (")+GetTimeSinceProgramStart()+").\n");
629
630    Console->Print(cf::va("Face Children    : %10lu    Draw World Outer Point Samples: %5lu\n", World.BspTree->FaceChildren.Size(), DrawWorldOutsidePointSamples.Size()));
631    Console->Print(cf::va("PointLights      : %10lu\n", World.PointLights.Size()));
632    Console->Print(cf::va("InfoPlayerStarts : %10lu\n", World.InfoPlayerStarts.Size()));
633    Console->Print(cf::va("Other Children   : %10lu\n", World.BspTree->OtherChildren.Size()));
634    Console->Print(cf::va("GameEntities     : %10lu\n", World.GameEntities.Size()));
635}
Note: See TracBrowser for help on using the browser.