root/cafu/trunk/CaLight/Init2.cpp

Revision 455, 19.5 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
22struct SunT
23{
24    MaterialT* Material;
25    VectorT    SunIrradiance;
26    VectorT    LightRevDir;
27};
28
29
30void InitSunlight(cf::PatchMeshT& PatchMesh, const ArrayT< ArrayT<Vector3dT> >& SampleCoords, const ArrayT<SunT>& Suns, const ArrayT<const cf::SceneGraph::FaceNodeT*>& SkyFaces, const CaLightWorldT& CaLightWorld)
31{
32    for (unsigned long PatchNr=0; PatchNr<PatchMesh.Patches.Size(); PatchNr++)
33    {
34        cf::PatchT&              Patch=PatchMesh.Patches[PatchNr];
35        const ArrayT<Vector3dT>& sc   =SampleCoords[PatchNr];
36
37        if (sc.Size()==0) continue;
38
39        for (unsigned long SunNr=0; SunNr<Suns.Size(); SunNr++)
40        {
41            if (dot(Patch.Normal, Suns[SunNr].LightRevDir)<=0.0) continue;
42
43            unsigned long NrOfSkyHits=0;
44
45            for (unsigned long SampleNr=0; SampleNr<sc.Size(); SampleNr++)
46            {
47                // Teste, ob der Strahl sc[SampleNr]+r*(-Suns[SunNr].LightDir) eine Face mit Sky-Texture trifft.
48                const VectorT Ray=Suns[SunNr].LightRevDir*9999999.9;
49                const VectorT Hit=sc[SampleNr]+Ray*CaLightWorld.TraceRay(sc[SampleNr], Ray);
50
51                // Teste, ob 'Hit' in einer Face mit Sky-Texture liegt.
52                unsigned long FNr;
53
54                for (FNr=0; FNr<SkyFaces.Size(); FNr++)
55                {
56                    const cf::SceneGraph::FaceNodeT* SkyFace=SkyFaces[FNr];
57
58                    if (SkyFace->Material!=Suns[SunNr].Material) continue;              // In diesem Durchlauf tragen nur solche SkyFaces bei, die zu Suns[SunNr] gehören.
59                    if (fabs(SkyFace->Polygon.Plane.GetDistance(Hit))>0.2) continue;    // Ist Hit zu weit von der SkyFace weg?
60
61                    unsigned long VNr;
62
63                    for (VNr=0; VNr<SkyFace->Polygon.Vertices.Size(); VNr++)
64                        if (SkyFace->Polygon.GetEdgePlane(VNr, 0.0).GetDistance(Hit)<-0.1) break;
65
66                    if (VNr==SkyFace->Polygon.Vertices.Size()) break;
67                }
68
69                if (FNr<SkyFaces.Size()) NrOfSkyHits++;
70            }
71
72            const double  Amount  =double(NrOfSkyHits)/double(sc.Size())*dot(Suns[SunNr].LightRevDir, Patch.Normal);
73            const VectorT SunLight=scale(Suns[SunNr].SunIrradiance, REFLECTIVITY*Amount);
74
75            Patch.UnradiatedEnergy+=SunLight;
76            Patch.TotalEnergy     +=SunLight;
77            Patch.EnergyFromDir   +=Suns[SunNr].LightRevDir*Max3(SunLight);
78        }
79    }
80}
81
82
83void InitializePatches(const CaLightWorldT& CaLightWorld)
84{
85    const cf::SceneGraph::BspTreeNodeT& Map=CaLightWorld.GetBspTree();
86
87    printf("\n%-50s %s\n", "*** Initialize Patches ***", GetTimeSinceProgramStart());
88
89
90    // Bilde zuerst ein LookUp-Array, das die Nummern aller Faces mit Radiosity-Sunlight Material enthält.
91    ArrayT<const cf::SceneGraph::FaceNodeT*> SkyFaces;
92
93    for (unsigned long FaceNr=0; FaceNr<Map.FaceChildren.Size(); FaceNr++)
94    {
95        const cf::SceneGraph::FaceNodeT* Face=Map.FaceChildren[FaceNr];
96
97        if (length(VectorT(Face->Material->meta_SunLight_Irr))<0.1) continue;
98        if (length(VectorT(Face->Material->meta_SunLight_Dir))<0.1) continue;
99
100        SkyFaces.PushBack(Face);
101    }
102
103    // Und daraus bilde eine Liste der vorkommenden/verwendeten Materials, in der jedes Material "unique" ist, also nur ein Mal vorkommt.
104    ArrayT<SunT> Suns;
105
106    for (unsigned long FaceNr=0; FaceNr<SkyFaces.Size(); FaceNr++)
107    {
108        const cf::SceneGraph::FaceNodeT* SkyFace=SkyFaces[FaceNr];
109
110        // See if the material of this SkyFace is already listed among the Suns materials.
111        unsigned long SunNr;
112        for (SunNr=0; SunNr<Suns.Size(); SunNr++)
113            if (Suns[SunNr].Material==SkyFace->Material) break;
114
115        if (SunNr<Suns.Size()) continue;
116
117        // It was not listed, so add it now.
118        Suns.PushBackEmpty();
119        Suns[SunNr].Material     =SkyFace->Material;
120        Suns[SunNr].SunIrradiance=Vector3T<double>(SkyFace->Material->meta_SunLight_Irr);
121        Suns[SunNr].LightRevDir  =-normalize(VectorT(SkyFace->Material->meta_SunLight_Dir), 0.0);
122    }
123
124
125    PatchMeshes.Clear();
126    unsigned long PatchMeshNr=0;
127    unsigned long PatchesCount=0;
128
129    for (unsigned long FaceNr=0; FaceNr<Map.FaceChildren.Size(); FaceNr++)
130    {
131        printf("%5.1f%% (f)\r", (double)FaceNr/(Map.FaceChildren.Size()+Map.OtherChildren.Size())*100.0);
132        fflush(stdout);
133
134        ArrayT< ArrayT< ArrayT<Vector3dT> > > SampleCoords;
135
136        Map.FaceChildren[FaceNr]->CreatePatchMeshes(PatchMeshes, SampleCoords);
137
138        for (unsigned long SampleCoordsNr=0; SampleCoordsNr<SampleCoords.Size(); SampleCoordsNr++)
139        {
140            InitSunlight(PatchMeshes[PatchMeshNr], SampleCoords[SampleCoordsNr], Suns, SkyFaces, CaLightWorld);
141
142            PatchesCount+=PatchMeshes[PatchMeshNr].Patches.Size();
143            PatchMeshNr++;
144        }
145    }
146
147    for (unsigned long OtherNr=0; OtherNr<Map.OtherChildren.Size(); OtherNr++)
148    {
149        printf("%5.1f%% (o)\r", (double)(Map.FaceChildren.Size()+OtherNr)/(Map.FaceChildren.Size()+Map.OtherChildren.Size())*100.0);
150        fflush(stdout);
151
152        ArrayT< ArrayT< ArrayT<Vector3dT> > > SampleCoords;
153
154        Map.OtherChildren[OtherNr]->CreatePatchMeshes(PatchMeshes, SampleCoords);
155
156        for (unsigned long SampleCoordsNr=0; SampleCoordsNr<SampleCoords.Size(); SampleCoordsNr++)
157        {
158            InitSunlight(PatchMeshes[PatchMeshNr], SampleCoords[SampleCoordsNr], Suns, SkyFaces, CaLightWorld);
159
160            PatchesCount+=PatchMeshes[PatchMeshNr].Patches.Size();
161            PatchMeshNr++;
162        }
163    }
164
165// THIS WAS REMOVED, BECAUSE:
166// I'm in the progress to introduce a Scene Graph for Cafu.
167// This means that the contents of BSP leaves will be described by an array of cf::SceneGraph::GenericNodeT* and terrains
168// are not explicitly mentioned anymore. Terrain support is temporarily removed, and will be implicitly added later again.
169#if 0
170    for (unsigned long TerrainNr=0; TerrainNr<CaLightWorld.TerrainEntities.Size(); TerrainNr++)
171    {
172        printf("%5.1f%%\r", (double)TerrainNr/CaLightWorld.TerrainEntities.Size()*100.0);
173        fflush(stdout);
174
175        const TerrainT::VertexT*     TerrainVertices=CaLightWorld.TerrainEntities[TerrainNr].Terrain.GetVertices();
176        const unsigned long          TerrainSize    =CaLightWorld.TerrainEntities[TerrainNr].Terrain.GetSize();
177        const BoundingBox3T<double>& TerrainBB      =CaLightWorld.TerrainEntities[TerrainNr].BB;
178        const FaceT&                 TerrainFace    =TerrainFaces[TerrainNr];
179        const unsigned long          Step           =(TerrainSize-1)/TerrainFace.LightMapInfo.SizeS;
180
181        if (Step==0) Error("A terrain cell is covered by multiple lightmap elements.");
182
183
184        for (unsigned long t=0; t<TerrainFace.LightMapInfo.SizeT; t++)
185            for (unsigned long s=0; s<TerrainFace.LightMapInfo.SizeS; s++)
186            {
187                const VectorT& V1=TerrainVertices[(s  )*Step+TerrainSize*(t  )*Step];
188                const VectorT& V2=TerrainVertices[(s  )*Step+TerrainSize*(t+1)*Step];
189                const VectorT& V3=TerrainVertices[(s+1)*Step+TerrainSize*(t+1)*Step];
190                const VectorT& V4=TerrainVertices[(s+1)*Step+TerrainSize*(t  )*Step];
191
192                Plane3T<double> Plane1;
193                Plane3T<double> Plane2;
194
195                if ((s & 1)==(t & 1))
196                {
197                    // The diagonal goes from lower left to upper right ( (0, 0) to (1, 1) ), yielding triangles (V1, V2, V3) and (V3, V4, V1).
198                    Plane1=Plane3T<double>(V1, V2, V3, 0.0);
199                    Plane2=Plane3T<double>(V3, V4, V1, 0.0);
200                }
201                else
202                {
203                    // The diagonal goes from upper left to lower right ( (0, 1) to (1, 0) ), yielding triangles (V1, V2, V4) and (V2, V3, V4).
204                    Plane1=Plane3T<double>(V1, V2, V4, 0.0);
205                    Plane2=Plane3T<double>(V2, V3, V4, 0.0);
206                }
207
208                const VectorT AvgNormal      =Plane1.Normal+Plane2.Normal;
209                const double  AvgNormalLength=length(AvgNormal);
210
211                // Store the patches in "image order" (y-axis pointing down / towards us),
212                // not in "world order" (y-axis pointing up / away from us).
213                // This is done because the terrains base texture is stored in the same way,
214                // and so in the engine we can use the same texture coordinates for rendering.
215                // Otherwise, a separate set of texture coordinates had to be created.
216                PatchT&  Patch       =Patches[Map.Faces.Size()+TerrainNr][(TerrainFace.LightMapInfo.SizeT-1-t)*TerrainFace.LightMapInfo.SizeS+s];
217                VectorT& Patch_Normal=TerrainPatchesNormals[TerrainNr][(TerrainFace.LightMapInfo.SizeT-1-t)*TerrainFace.LightMapInfo.SizeS+s];
218
219                Patch.Coord     =scale(V1+V3, 0.5);   // V1+V3 or V2+V4 doesn't matter here, due to the "safety" correction below.
220                Patch_Normal    =AvgNormalLength>0.01 ? scale(AvgNormal, 1.0/AvgNormalLength) : VectorT(0.0, 0.0, 1.0); if (AvgNormalLength<=0.01) printf("INFO: AvgNormalLength<=0.01.\n");
221                Patch.InsideFace=true;
222
223                // Make sure that Patch.Coord is *above* the terrain (due to the "Step", it could be below!).
224                const VectorT TraceOrigin=VectorT(Patch.Coord.x, Patch.Coord.y, TerrainBB.Max.z+100.0);
225                const VectorT TraceDir   =VectorT(0.0, 0.0, TerrainBB.Min.z-TerrainBB.Max.z-200.0);
226                VB_Trace3T<double> TraceResult(1.0);
227
228                CaLightWorld.TerrainEntities[TerrainNr].Terrain.TraceBoundingBox(BoundingBox3T<double>(Vector3dT()), TraceOrigin, TraceDir, TraceResult);
229                Patch.Coord.z=TraceOrigin.z+TraceDir.z*TraceResult.Fraction+0.19;
230                if (TraceResult.Fraction==1.0)
231                    printf("WARNING: TraceResult.Fraction==1.0 in %s at line %u! (Terrain %lu at (%lu,%lu).)\n", __FILE__, __LINE__, TerrainNr, s, t);
232
233
234                // EINSCHUB: Betrachte bei dieser Gelegenheit auch gleich den Einfall des Sonnenlichts.
235                for (unsigned long SunNr=0; SunNr<Suns.Size(); SunNr++)
236                {
237                    if (dot(Patch_Normal, Suns[SunNr].LightRevDir)<=0.0) continue;
238
239                    // Notes:
240                    // 1. It is not adviseable to add any other sample points, as their validity had to be verified as Patch.Coord had been!
241                    // 2. We *could* use individual normal vectors for each sample point, but that is probably not worth the effort.
242                    ArrayT<VectorT> SampleCoords;
243
244                    SampleCoords.PushBack(V1+VectorT(0.0, 0.0, 0.19));
245                    SampleCoords.PushBack(V2+VectorT(0.0, 0.0, 0.19));
246                    SampleCoords.PushBack(V3+VectorT(0.0, 0.0, 0.19));
247                    SampleCoords.PushBack(V4+VectorT(0.0, 0.0, 0.19));
248                    SampleCoords.PushBack(Patch.Coord);
249
250                    unsigned long NrOfSkyHits=0;
251                    for (unsigned long SampleNr=0; SampleNr<SampleCoords.Size(); SampleNr++)
252                    {
253                        // Teste, ob der Strahl SampleCoords[SampleNr]+r*(-SunLightDir) eine Face mit Sky-Texture trifft,
254                        // taking terrains into account.
255                        const double  r  =CaLightWorld.Map.ClipLine(SampleCoords[SampleNr], Suns[SunNr].LightRevDir, 0.1, 9999999.9, CaLightWorld.TerrainEntities.Size()>0 ? &CaLightWorld.TerrainEntities[0] : NULL);
256                        const VectorT Hit=SampleCoords[SampleNr]+scale(Suns[SunNr].LightRevDir, r);
257
258                        // Teste, ob 'Hit' in einer Face mit Sky-Texture liegt.
259                        unsigned long FNr;
260
261                        for (FNr=0; FNr<SkyFaces.Size(); FNr++)
262                        {
263                            const FaceT& SkyFace=Map.Faces[SkyFaces[FNr]];
264
265                            if (SkyFace.Material!=Suns[SunNr].Material) continue;       // In diesem Durchlauf tragen nur solche SkyFaces bei, die zu Suns[SunNr] gehören.
266                            if (fabs(SkyFace.Plane.GetDistance(Hit))>0.2) continue;  // Ist Hit zu weit von der SkyFace weg?
267
268                            unsigned long VNr;
269
270                            for (VNr=0; VNr<SkyFace.Vertices.Size(); VNr++)
271                                if (SkyFace.GetEdgePlane(VNr, 0.0).GetDistance(Hit)<-0.1) break;
272
273                            if (VNr==SkyFace.Vertices.Size()) break;
274                        }
275
276                        if (FNr<SkyFaces.Size()) NrOfSkyHits++;
277                    }
278
279                    const double  Amount  =double(NrOfSkyHits)/double(SampleCoords.Size())*dot(Suns[SunNr].LightRevDir, Patch_Normal);
280                    const double  Ambient =0.1;     // Fraction of "artificial" ambient light.
281                    const VectorT SunLight=scale(Suns[SunNr].SunIrradiance, Ambient+REFLECTIVITY*Amount*(1.0-Ambient));
282
283                    Patch.UnradiatedEnergy=Patch.UnradiatedEnergy+SunLight;
284                    Patch.TotalEnergy     =Patch.TotalEnergy     +SunLight;
285                    Patch.EnergyFromDir   =Patch.EnergyFromDir   +Suns[SunNr].LightRevDir*Max3(SunLight);
286                }
287            }
288
289
290        // For the purpose of debugging, export the terrains normal map vectors into a normal-map image.
291        /* BitmapT NormalMap;
292
293        NormalMap.SizeX=TerrainFace.LightMapInfo.SizeS;
294        NormalMap.SizeY=TerrainFace.LightMapInfo.SizeT;
295
296        for (unsigned long NormalNr=0; NormalNr<TerrainPatchesNormals[TerrainNr].Size(); NormalNr++)
297        {
298            const unsigned long r=(unsigned long)((TerrainPatchesNormals[TerrainNr][NormalNr].x+1.0)*0.5*255.0+0.49); if (r>255) printf("r>255.\n");
299            const unsigned long g=(unsigned long)((TerrainPatchesNormals[TerrainNr][NormalNr].y+1.0)*0.5*255.0+0.49); if (g>255) printf("g>255.\n");
300            const unsigned long b=(unsigned long)((TerrainPatchesNormals[TerrainNr][NormalNr].z+1.0)*0.5*255.0+0.49); if (b>255) printf("b>255.\n");
301            const unsigned long a=255;
302
303            NormalMap.Data.PushBack((a << 24) + (b << 16) + (g << 8) + (r << 0));
304        }
305
306        NormalMap.SaveToDisk("Terrain_norm.png"); */
307    }
308#endif
309    printf("# patches allocated:%10lu\n", PatchesCount);
310    printf("Patch coords and sunlight information calculated.\n");
311
312    // 3. Trotz der vielen SamplePoints für jeden Patch kann das Sonnenlicht ziemlich eckig wirken.
313    //    Deshalb filtern wir es hier vorsichtig nach, indem wir für jeden Patch das gewichtete Mittel mit den acht umliegenden Patches bilden.
314    //    Die vier unmittelbar anliegenden Patches werden mit 1/4 gewichtet, die vier Ecken mit 1/16.
315    for (unsigned long PatchMeshNr=0; PatchMeshNr<PatchMeshes.Size(); PatchMeshNr++)
316    {
317        cf::PatchMeshT& PM=PatchMeshes[PatchMeshNr];
318
319        ArrayT<VectorT> UnradEBuffer;
320        ArrayT<VectorT> TotalEBuffer;
321        ArrayT<VectorT> EFrDirBuffer;
322
323        for (unsigned long PatchNr=0; PatchNr<PM.Patches.Size(); PatchNr++)
324        {
325            UnradEBuffer.PushBack(PM.Patches[PatchNr].UnradiatedEnergy);
326            TotalEBuffer.PushBack(PM.Patches[PatchNr].TotalEnergy);
327            EFrDirBuffer.PushBack(PM.Patches[PatchNr].EnergyFromDir);
328        }
329
330        for (unsigned long t=0; t<PM.Height; t++)
331            for (unsigned long s=0; s<PM.Width; s++)
332            {
333                cf::PatchT& Patch=PM.GetPatch(s, t);
334
335                if (!Patch.InsideFace) continue;
336
337                VectorT UnradAverage=Patch.UnradiatedEnergy;
338                VectorT TotalAverage=Patch.TotalEnergy;
339                VectorT FrDirAverage=Patch.EnergyFromDir;
340                double  CoveredArea =1.0;
341
342                // Der Patch liegt in der Mitte eines 3x3-Feldes bei Koordinate (1,1).
343                // Darüber stellen wir uns ein 2x2-Feld vor, dessen Mitte mit der Mitte des mittleren 3x3-Feldes (dem betrachteten Patch) zusammenfällt.
344                // Aus den Überlappungen der beiden Felder ergeben sich die Gewichte für die Felder des 3x3-Feldes: Die Mitte 100%, die Ecken 25%,
345                // die anderen Felder (anliegend: oben, unten, links, rechts) 50%. Da wir nur wenig filtern wollen, verkleinern wir das 2x2-Feld, indem
346                // wir die Gewichte quadrieren: Mitte 100%, Ecken 6,25%, Rest 25%.
347                for (char y=0; y<=2; y++)
348                    for (char x=0; x<=2; x++)
349                    {
350                        if (x==1 && y==1) continue;     // Patch selbst ist schon dazugezählt, nur noch die umliegenden Patches betrachten
351
352                        int Nx=int(s+x)-1;
353                        int Ny=int(t+y)-1;
354
355                        if (PM.WrapsHorz)
356                        {
357                            // Patches that wrap do *not* duplicate the leftmost column at the right.
358                            if (Nx<             0) Nx+=PM.Width;
359                            if (Nx>=int(PM.Width)) Nx-=PM.Width;
360                        }
361
362                        if (PM.WrapsVert)
363                        {
364                            // Patches that wrap do *not* duplicate the topmost column at the bottom.
365                            if (Ny<              0) Ny+=PM.Height;
366                            if (Ny>=int(PM.Height)) Ny-=PM.Height;
367                        }
368
369                        if (Nx<              0) continue;   // Linken  Rand beachten.
370                        if (Nx>= int(PM.Width)) continue;   // Rechten Rand beachten.
371                        if (Ny<              0) continue;   // Oberen  Rand beachten.
372                        if (Ny>=int(PM.Height)) continue;   // Unteren Rand beachten.
373
374                        if (!PM.GetPatch(Nx, Ny).InsideFace) continue;
375
376                        const double RelevantArea=(x!=1 && y!=1) ? 0.25*0.25 : 0.5*0.5;
377
378                        UnradAverage=UnradAverage+scale(UnradEBuffer[Ny*PM.Width+Nx], RelevantArea);
379                        TotalAverage=TotalAverage+scale(TotalEBuffer[Ny*PM.Width+Nx], RelevantArea);
380                        FrDirAverage=FrDirAverage+scale(EFrDirBuffer[Ny*PM.Width+Nx], RelevantArea);
381                        CoveredArea+=RelevantArea;
382                    }
383
384                Patch.UnradiatedEnergy=scale(UnradAverage, 1.0/CoveredArea);
385                Patch.TotalEnergy     =scale(TotalAverage, 1.0/CoveredArea);
386                Patch.EnergyFromDir   =scale(FrDirAverage, 1.0/CoveredArea);
387            }
388    }
389    printf("Sunlight smoothed.\n");
390}
Note: See TracBrowser for help on using the browser.