root/cafu/trunk/CaSHL/CaSHL.cpp

Revision 455, 64.1 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/***                                          ***/
24/*** Cafu Spherical Harmonic Lighting Utility ***/
25/***                                          ***/
26/*** Der Herr sprach                          ***/
27/***   Es werde Licht!                        ***/
28/*** Und es wurde Licht.                      ***/
29/*** Genesis                                  ***/
30/***                                          ***/
31/************************************************/
32
33// ALLGEMEINE BEMERKUNGEN ZU FACES, SHLMAPS UND PATCHES:
34// Wir definieren eine SHLMap als ein Rechteck aus s*t quadratischen Patches, die jeweils eine Face "abdecken".
35// Man stelle sich die unendlich große Ebene, in der die Face liegt, als von quadratischen Patches überzogen vor,
36// wie ein kariertes Mathe-Schulheft. Wichtig ist nun, daß der Ursprung des Patch-Rasters *ganzzahlig* mit dem
37// World-Origin zusammenfällt. D.h., wenn man die Ebene entlang ihres Normalenvektors solange verschiebt, bis der
38// World-Origin in ihr liegt, muß ein Schnittpunkt des Patch-Rasters damit zusammenfallen.
39// Im Gegensatz zu einem früheren Ansatz, bei dem die Verschiebung des Patch-Rasters sich an der kleinsten s- und t-Koordinate
40// der Face orientiert hat, stellen wir mit diesem Vorgehen sicher, daß sich Patches von benachbarte Faces stets *vollständig*
41// überlappen (oder gar nicht). Beliebige teilweise Überlappungen kommen nicht mehr vor.
42// Das Rechteck sollte bei gegebener Seitenlänge der Patches und gegebener Orientierung (entlang des UV-Koordinatensystems,
43// welches man mit PlaneT::GetSpanVectors() erhält) möglichst kleine s- und t-Abmessungen haben.
44// Außerdem ziehen wir noch einen 1 Patch breiten Rahmen drumherum. Damit soll dem OpenGL-Renderer Rechnung getragen werden,
45// der zu jeder (s,t)-Koordinate den Mittelwert des umliegenden 2x2-Quadrats bestimmt (bilinear Filtering).
46// Betrachte dazu auch die Darstellung im Cafu Tech-Archive vom 28. Oktober 2003.
47
48#ifdef _WIN32
49#define WIN32_LEAN_AND_MEAN
50#include <windows.h>
51#include <conio.h>
52#include <direct.h>
53#else
54#include <stdarg.h>
55#include <unistd.h>
56#include <string.h>
57#include <dirent.h>
58#define _stricmp strcasecmp
59#define _getch getchar
60#endif
61
62#include <time.h>
63#include <stdio.h>
64
65#include "Templates/Array.hpp"
66#include "ConsoleCommands/Console.hpp"
67#include "ConsoleCommands/ConsoleInterpreter.hpp"
68#include "ConsoleCommands/ConsoleStdout.hpp"
69#include "FileSys/FileManImpl.hpp"
70#include "Math3D/BoundingBox.hpp"
71#include "Math3D/Vector3.hpp"
72#include "Bitmap/Bitmap.hpp"
73#include "MaterialSystem/Material.hpp"
74#include "MaterialSystem/MaterialManager.hpp"
75#include "MaterialSystem/MaterialManagerImpl.hpp"
76#include "Models/ModelManager.hpp"
77#include "SceneGraph/Node.hpp"
78#include "SceneGraph/BspTreeNode.hpp"
79#include "SceneGraph/FaceNode.hpp"
80#include "ClipSys/CollisionModelMan_impl.hpp"
81
82#include "CaSHLWorld.hpp"
83
84#if defined(_WIN32)
85    #if defined(_MSC_VER)
86        #define vsnprintf _vsnprintf
87    #endif
88#endif
89
90
91static cf::ConsoleStdoutT ConsoleStdout;
92cf::ConsoleI* Console=&ConsoleStdout;
93
94static cf::FileSys::FileManImplT FileManImpl;
95cf::FileSys::FileManI* cf::FileSys::FileMan=&FileManImpl;
96
97static cf::ClipSys::CollModelManImplT CCM;
98cf::ClipSys::CollModelManI* cf::ClipSys::CollModelMan=&CCM;
99
100ConsoleInterpreterI* ConsoleInterpreter=NULL;
101MaterialManagerI*    MaterialManager   =NULL;
102
103
104const time_t ProgramStartTime=time(NULL);
105
106// Returns a string with the elapsed time since program start.
107// The string is in the format "hh:mm:ss".
108static const char* GetTimeSinceProgramStart()
109{
110    const unsigned long TotalSec=(unsigned long)difftime(time(NULL), ProgramStartTime);
111    const unsigned long Sec     =TotalSec % 60;
112    const unsigned long Min     =(TotalSec/60) % 60;
113    const unsigned long Hour    =TotalSec/3600;
114
115    static char TimeString[16];
116    sprintf(TimeString, "%2lu:%2lu:%2lu", Hour, Min, Sec);
117
118    return TimeString;
119}
120
121
122static void Error(const char* ErrorText, ...)
123{
124    va_list ArgList;
125    char    ErrorString[256];
126
127    if (ErrorText!=NULL)
128    {
129        va_start(ArgList, ErrorText);
130            vsnprintf(ErrorString, 256, ErrorText, ArgList);
131        va_end(ArgList);
132
133        printf("\nFATAL ERROR: %s\n", ErrorString);
134    }
135
136    printf("Program aborted.\n\n");
137    exit(1);
138}
139
140
141const double REFLECTIVITY=0.3;  // Gleiche Reflektivität für alle Faces und für alle Wellenlängen
142const double Pi          =3.14159265358979323846;
143
144// Defined in CaSHLWorld.hpp now!
145//
146// struct PatchT
147// {
148//     VectorT UnradiatedEnergy;       // Noch nicht in die Umgebung abgestrahlte Energie     (per unit time per unit area)
149//     VectorT TotalEnergy;            // Energie, die dieser Patch in die Umgebung abstrahlt (per unit time per unit area)
150//
151//     VectorT Coord;                  // Position (+safety) in world space of the center of the patch. Valid only if InsideFace==true.
152//     bool    InsideFace;             // InsideFace==true <==> this patch is not completely outside its face
153// };
154
155enum FaceVis { NO_VISIBILITY, PARTIAL_VISIBILITY, FULL_VISIBILITY };
156ArrayT< ArrayT<FaceVis> > FacePVS;  // The set of faces each face can see (see InitializeFacePVSMatrix() for a description!)
157ArrayT< ArrayT<PatchT > > Patches;  // Each face gets a set of patches, PatchT is declared in CaSHLWorld.hpp
158
159
160#include "Init1.cpp"    // void InitializeFacePVSMatrix(const cf::SceneGraph::BspTreeNodeT& Map                         ) { ... }
161#include "Init2.cpp"    // void InitializePatches      (const cf::SceneGraph::BspTreeNodeT& Map, const SkyDomeT& SkyDome) { ... }
162
163
164// Strahlt den Transfer der Patches im n*n Quadrat ab, dessen linke obere Ecke bei (s_i, t_i) liegt.
165void RadiateTransfer(const CaSHLWorldT& CaSHLWorld, unsigned long Face_i, unsigned long s_i, unsigned long t_i, char n)
166{
167    const cf::SceneGraph::BspTreeNodeT& Map  =CaSHLWorld.GetBspTree();
168    const unsigned long       NR_OF_SH_COEFFS=cf::SceneGraph::SHLMapManT::NrOfBands * cf::SceneGraph::SHLMapManT::NrOfBands;
169    const double              PATCH_SIZE     =cf::SceneGraph::FaceNodeT::SHLMapInfoT::PatchSize;
170    const cf::SceneGraph::FaceNodeT::SHLMapInfoT& SMI=Map.FaceChildren[Face_i]->SHLMapInfo;
171
172    unsigned long  Big_P_i_Count=0;
173    VectorT        Big_P_i_Coord;
174#if USE_NORMALMAPS
175    VectorT        Big_P_i_Normal;
176#endif
177    ArrayT<double> Big_P_i_SHCoeffs_UnradiatedTransfer;
178
179    while (Big_P_i_SHCoeffs_UnradiatedTransfer.Size()<NR_OF_SH_COEFFS) Big_P_i_SHCoeffs_UnradiatedTransfer.PushBack(0.0);
180
181    // Bilde den Positions-Durchschnitt bzw. die UnradiatedTransfer-Summe aller Patches im n*n Quadrat,
182    // wobei (s_i, t_i) die linke obere Ecke ist und nur Patches innerhalb der Face berücksichtigt werden.
183    for (char y=0; y<n; y++)
184        for (char x=0; x<n; x++)
185        {
186            if (s_i+x+1>SMI.SizeS) continue;
187            if (t_i+y+1>SMI.SizeT) continue;
188
189            PatchT& P_i=Patches[Face_i][(t_i+y)*SMI.SizeS+(s_i+x)];
190            if (!P_i.InsideFace) continue;
191
192            Big_P_i_Count++;
193            Big_P_i_Coord =Big_P_i_Coord +P_i.Coord;
194#if USE_NORMALMAPS
195            Big_P_i_Normal=Big_P_i_Normal+P_i.Normal;
196#endif
197
198            for (unsigned long CoeffNr=0; CoeffNr<P_i.SHCoeffs_UnradiatedTransfer.Size(); CoeffNr++)
199            {
200                Big_P_i_SHCoeffs_UnradiatedTransfer[CoeffNr]+=P_i.SHCoeffs_UnradiatedTransfer[CoeffNr];
201
202                // By being added to the Big_P_i above, this patch has radiated its transfer. Job done. Mission accomplished.
203                P_i.SHCoeffs_UnradiatedTransfer[CoeffNr]=0.0;
204            }
205        }
206
207    if (!Big_P_i_Count) return;
208    Big_P_i_Coord =scale(Big_P_i_Coord, 1.0/double(Big_P_i_Count));
209#if USE_NORMALMAPS
210    Big_P_i_Normal=normalize(Big_P_i_Normal, 0.0);
211#endif
212
213
214    // Betrachte alle Patches aller Faces im PVS der Face Face_i.
215    for (unsigned long Face_j=0; Face_j<Map.FaceChildren.Size(); Face_j++)
216    {
217        // Vermeide alle unnötigen und evtl. rundungsfehlergefährdeten Berechnungen.
218        // Die folgende Zeile fängt auch alle Fälle ab, in denen Face_j in der Ebene von Face_i liegt
219        // und insb. für die Face_i==Face_j gilt. Vgl. die Erstellung und Optimierung der FacePVS-Matrix!
220        if (FacePVS[Face_i][Face_j]==NO_VISIBILITY) continue;
221        if (Map.FaceChildren[Face_j]->Polygon.Plane.GetDistance(Big_P_i_Coord)<0.1) continue;
222
223        bool ExplicitTestRequired=(FacePVS[Face_i][Face_j]!=FULL_VISIBILITY);
224
225        for (unsigned long Patch_j=0; Patch_j<Patches[Face_j].Size(); Patch_j++)
226        {
227            PatchT& P_j=Patches[Face_j][Patch_j];
228            if (!P_j.InsideFace) continue;
229
230            // Einsparen des Wurzelziehens: Rechne einfach mit dem Quadrat weiter!
231            const VectorT Ray       =P_j.Coord-Big_P_i_Coord;
232         // double        RayLength =length(Ray);
233            double        RayLength2=dot(Ray, Ray);
234
235         // if (RayLength <0.5    ) continue;   // Kommt das jemals vor?
236            if (RayLength2<0.5*0.5) continue;
237
238         // VectorT Dir_ij =scale(Ray, 1.0/RayLength );
239            VectorT Dir_ij2=scale(Ray, 1.0/RayLength2);   // Dir_ij2==Dir_ij/RayLength
240
241            if (ExplicitTestRequired)
242                if (CaSHLWorld.TraceRay(Big_P_i_Coord, Ray)<1.0) continue;
243
244            if (RayLength2<PATCH_SIZE*PATCH_SIZE)
245            {
246                RayLength2=PATCH_SIZE*PATCH_SIZE;
247                Dir_ij2   =scale(Ray, 1.0/RayLength2);
248            }
249
250#if USE_NORMALMAPS
251            const double cos1__= dot(Map.FaceChildren[Face_i]->Polygon.Plane.Normal, Dir_ij2); if (cos1__<=0) continue;
252            const double cos2__=-dot(Map.FaceChildren[Face_j]->Polygon.Plane.Normal, Dir_ij2); if (cos2__<=0) { printf("cos2__<=0\n"); continue; }   // Sollte niemals vorkommen (wg. PlaneDist-Check oben)!
253            const double cos1_ = dot(Big_P_i_Normal, Dir_ij2); if (cos1_ <=0) continue;
254            const double cos2_ =-dot(P_j.Normal    , Dir_ij2); if (cos2_ <=0) continue;
255#else
256         // const double cos1 = dot(Map.FaceChildren[Face_i]->Polygon.Plane.Normal, Dir_ij ); if (cos1 <=0) continue;
257         // const double cos2 =-dot(Map.FaceChildren[Face_j]->Polygon.Plane.Normal, Dir_ij ); if (cos2 <=0) { printf("cos2 <=0\n"); continue; }   // Sollte niemals vorkommen (wg. PlaneDist-Check oben)!
258            const double cos1_= dot(Map.FaceChildren[Face_i]->Polygon.Plane.Normal, Dir_ij2); if (cos1_<=0) continue;
259            const double cos2_=-dot(Map.FaceChildren[Face_j]->Polygon.Plane.Normal, Dir_ij2); if (cos2_<=0) { printf("cos2_<=0\n"); continue; }   // Sollte niemals vorkommen (wg. PlaneDist-Check oben)!
260#endif
261
262            // 'Alternative', einfache Herleitung des Form-Faktors:
263            // Betrachte die Halbkugel über dem Patch i mit Radius RayLength. RayLength soll groß genug sein,
264            // d.h. Patch j soll problemlos als ein Teil der Halbkugeloberfläche betrachtet werden können.
265            // Die prozentuale Sichtbarkeit erhalten wir also sofort aus A_j/O, wobei A_j der Flächeninhalt des Patches j ist
266            // und O der Oberflächeninhalt der Halbkugel, O=0.5*4*pi*RayLength^2.
267            // cos1 und cos2 berücksichtigen dann noch die gegenseitige Verdrehung der Patches und wir sind fertig.
268            // Einziges Problem: Obige Herleitung enthält noch einen Faktor 1/2, für den ich leider keine Erklärung habe.
269            // Noch eine Alternative: Man muß RayLength ausdrücken in Patch-Längen, nicht in Millimetern!
270         // double FormFactor_ij=PATCH_SIZE*PATCH_SIZE/3.14159265359*cos1 *cos2 /(RayLength*RayLength);
271            double FormFactor_ij=PATCH_SIZE*PATCH_SIZE/3.14159265359*cos1_*cos2_;
272
273            // Die Flächeninhalte scheinen sich herauszukürzen!?
274            // (Im FormFactor ist P_j.Area/P_i.Area enthalten, und dieser wird hier multipliziert mit P_i.Area/P_j.Area.)
275            // Wir müssen nichtmal Big_P_i_Count hineinmultiplizieren, da Big_P_i_UnradiatedEnergy schon die Summe der Einzelpatches ist!
276            for (unsigned long CoeffNr=0; CoeffNr<Big_P_i_SHCoeffs_UnradiatedTransfer.Size(); CoeffNr++)
277            {
278                const double DeltaTransfer=Big_P_i_SHCoeffs_UnradiatedTransfer[CoeffNr]*REFLECTIVITY*FormFactor_ij;
279
280                P_j.SHCoeffs_UnradiatedTransfer[CoeffNr]+=DeltaTransfer;
281                P_j.SHCoeffs_TotalTransfer     [CoeffNr]+=DeltaTransfer;
282            }
283        }
284    }
285}
286
287
288// Computes the Associated Legendre Polynomial at 'x'.
289double ALP(int l, int m, double x)
290{
291    // Start with rule #2.
292    double Pmm  =1.0;
293    double S1mx2=sqrt(1.0-x*x);
294    double fact =1.0;
295
296    for (int i=0; i<m; i++)
297    {
298        Pmm  *= -1.0*fact*S1mx2;
299        fact +=  2.0;
300    }
301    if (l==m) return Pmm;
302
303    double Pmmp1=x*(2.0*m+1.0)*Pmm;
304    if (l==m+1) return Pmmp1;
305
306    double Pll=0.0;
307
308    for (int ll=m+2; ll<=l; ll++)
309    {
310        Pll  =(x*(2.0*ll-1.0)*Pmmp1 - (ll+m-1.0)*Pmm) / (ll-m);
311        Pmm  =Pmmp1;
312        Pmmp1=Pll;
313    }
314    return Pll;
315}
316
317
318double factorial(int n)
319{
320    static bool   IsInitialized=false;
321    static double Factorials[33];
322
323    if (!IsInitialized)
324    {
325        Factorials[0]=1.0;
326
327        for (char i=1; i<33; i++)
328            Factorials[i]=double(i)*Factorials[i-1];
329
330        IsInitialized=true;
331    }
332
333    return Factorials[n<33 ? n : 32];
334}
335
336
337// Computes a spherical harmonic function.
338// 'l'     is the band, range [0...N].
339// 'm'     is in range [-l...l].
340// 'theta' is the first polar angle, in range [0...Pi].
341// 'phi'   is the second polar angle, in range [0...2*Pi].
342// This function is described in detail in the paper by Robin Green.
343double SphericalHarmonic(int l, int m, double theta, double phi)
344{
345    const double K=sqrt((2.0*l+1.0)/(4.0*Pi) * factorial(l-abs(m))/factorial(l+abs(m)));
346
347         if (m>0) return sqrt(2.0)*cos( m*phi)*K*ALP(l,  m, cos(theta));
348    else if (m<0) return sqrt(2.0)*sin(-m*phi)*K*ALP(l, -m, cos(theta));
349    else /*m==0*/ return                       K*ALP(l,  0, cos(theta));
350}
351
352
353struct SphericalSampleT
354{
355 // VectorT        Angles;      // The position on the sphere in polar coordinates.
356    VectorT        UnitVector;  // The same position as a unit vector.
357    ArrayT<double> Coeffs;      // For an n-band approximation, these are the n^2 coefficients (values of the y(l, m, theta, phi) function).
358};
359
360
361void DirectLighting(const CaSHLWorldT& CaSHLWorld, const unsigned long SqrtNrOfSamples)
362{
363    const cf::SceneGraph::BspTreeNodeT& Map=CaSHLWorld.GetBspTree();
364
365    printf("\n%-50s %s\n", "*** PHASE I - performing direct lighting ***", GetTimeSinceProgramStart());
366
367
368    // This is a first test with SH lighting.
369    // In order to keep things initially simple, the following implements "Shadowed Diffuse Transfer",
370    // as detailed in Robin Greens paper on pages 29 and subsequent.
371    ArrayT<SphericalSampleT> SphericalSamples;
372    ArrayT<unsigned long>    SkyFaces;
373    unsigned long            FaceNr;
374    const unsigned long      NR_OF_SH_COEFFS=cf::SceneGraph::SHLMapManT::NrOfBands * cf::SceneGraph::SHLMapManT::NrOfBands;
375
376
377    // Bilde zuerst ein LookUp-Array, das die Nummern aller Faces mit Sky-Texture enthält.
378    for (FaceNr=0; FaceNr<Map.FaceChildren.Size(); FaceNr++)
379        if (length(Vector3T<double>(Map.FaceChildren[FaceNr]->Material->meta_SunLight_Irr))>0.1 &&
380            length(Vector3T<double>(Map.FaceChildren[FaceNr]->Material->meta_SunLight_Dir))>0.1) SkyFaces.PushBack(FaceNr);
381
382
383    for (unsigned long SampleX=0; SampleX<SqrtNrOfSamples; SampleX++)
384        for (unsigned long SampleY=0; SampleY<SqrtNrOfSamples; SampleY++)
385        {
386            const double x    =double(SampleX)/double(SqrtNrOfSamples) + 0.5/double(SqrtNrOfSamples);
387            const double y    =double(SampleY)/double(SqrtNrOfSamples) + 0.5/double(SqrtNrOfSamples);
388            const double theta=2.0*acos(sqrt(1.0-x));
389            const double phi  =2.0*Pi*y;
390
391            SphericalSamples.PushBackEmpty(1);
392         // SphericalSamples[SphericalSamples.Size()-1].Angles    =VectorT(theta, phi);
393            SphericalSamples[SphericalSamples.Size()-1].UnitVector=VectorT(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta));
394
395            for (int l=0; l<cf::SceneGraph::SHLMapManT::NrOfBands; l++)
396                for (int m=-l; m<=l; m++)   // const int Index=l*(l+1)+m;
397                    SphericalSamples[SphericalSamples.Size()-1].Coeffs.PushBack(SphericalHarmonic(l, m, theta, phi));
398
399            if (SphericalSamples[SphericalSamples.Size()-1].Coeffs.Size()!=NR_OF_SH_COEFFS) Error("Bad number of coeffs in %s, line %u.", __FILE__, __LINE__);
400        }
401
402
403    // I assume (think/guess/hope) that the range of the SphericalHarmonic function does not exceed [-1...1].
404    // It is important to know this range because we later want to efficiently store our computed results,
405    // e.g. as fixed-point values with limited precision (like compressed into a char).
406    // However, to be safe, a rigorous mathematically founded answer would be required, which im still lacking.
407    {
408        double Min=(SphericalSamples.Size()>0 && NR_OF_SH_COEFFS>0) ? SphericalSamples[0].Coeffs[0] : 0.0;
409        double Max=(SphericalSamples.Size()>0 && NR_OF_SH_COEFFS>0) ? SphericalSamples[0].Coeffs[0] : 0.0;
410
411        for (unsigned long SampleNr=0; SampleNr<SphericalSamples.Size(); SampleNr++)
412            for (unsigned long CoeffNr=0; CoeffNr<NR_OF_SH_COEFFS; CoeffNr++)
413            {
414                const double Value=SphericalSamples[SampleNr].Coeffs[CoeffNr];
415
416                if (Value<Min) Min=Value;
417                if (Value>Max) Max=Value;
418            }
419
420        printf("SHL INFO:  Min %15.10f    Max %15.10f\n", Min, Max);
421        if (Min<-1.0 || Max>1.0) Error("Assumed range of SphericalHarmonic() is out of bounds.");   // Should never happen...
422    }
423
424
425    for (FaceNr=0; FaceNr<Map.FaceChildren.Size(); FaceNr++)
426    {
427        const cf::SceneGraph::FaceNodeT::SHLMapInfoT& SMI=Map.FaceChildren[FaceNr]->SHLMapInfo;
428
429        printf("%5.1f%%\r", (double)FaceNr/Map.FaceChildren.Size()*100.0);
430        fflush(stdout);
431
432        for (unsigned long t=0; t<SMI.SizeT; t++)
433            for (unsigned long s=0; s<SMI.SizeS; s++)
434            {
435                PatchT& Patch=Patches[FaceNr][t*SMI.SizeS+s];
436
437                // A patch that is entirely outside of its face is not considered.
438                if (!Patch.InsideFace) continue;
439
440                for (unsigned long SampleNr=0; SampleNr<SphericalSamples.Size(); SampleNr++)
441                {
442#if USE_NORMALMAPS
443                    const double CosTerm =dot(SphericalSamples[SampleNr].UnitVector, Patch.Normal);
444                    const double CosTerm2=dot(SphericalSamples[SampleNr].UnitVector, Map.FaceChildren[FaceNr]->Polygon.Plane.Normal);
445
446                    if (CosTerm>0.0 && CosTerm2>0.0)
447#else
448                    const double CosTerm=dot(SphericalSamples[SampleNr].UnitVector, Map.FaceChildren[FaceNr]->Polygon.Plane.Normal);
449
450                    if (CosTerm>0.0)
451#endif
452                    {
453                        // The ray is in the upper hemisphere.
454                        // Now figure out if it hits a "sky" face.
455                        const VectorT Ray=SphericalSamples[SampleNr].UnitVector*9999999.9;
456                        const VectorT Hit=Patch.Coord+Ray*CaSHLWorld.TraceRay(Patch.Coord, Ray);
457
458                        // Teste, ob 'Hit' in einer Face mit Sky-Texture liegt.
459                        unsigned long FNr;
460
461                        for (FNr=0; FNr<SkyFaces.Size(); FNr++)
462                        {
463                            const Polygon3T<double>& SkyFace=Map.FaceChildren[SkyFaces[FNr]]->Polygon;
464
465                            if (fabs(SkyFace.Plane.GetDistance(Hit))>0.2) continue;  // Ist 'Hit' zu weit von der SkyFace weg?
466
467                            unsigned long VNr;
468                            for (VNr=0; VNr<SkyFace.Vertices.Size(); VNr++)
469                                if (SkyFace.GetEdgePlane(VNr, MapT::RoundEpsilon).GetDistance(Hit)<-0.1) break;
470
471                            if (VNr==SkyFace.Vertices.Size()) break;
472                        }
473
474                        if (FNr<SkyFaces.Size())
475                        {
476                            // The ray actually hit the sky!
477                            for (unsigned long CoeffNr=0; CoeffNr<NR_OF_SH_COEFFS; CoeffNr++)
478                                Patch.SHCoeffs_TotalTransfer[CoeffNr]+=CosTerm*SphericalSamples[SampleNr].Coeffs[CoeffNr];
479                        }
480                    }
481                }
482
483                // When done, all coefficients should be in range [-4*Pi...4*Pi].
484                for (unsigned long CoeffNr=0; CoeffNr<NR_OF_SH_COEFFS; CoeffNr++)
485                {
486                    Patch.SHCoeffs_TotalTransfer[CoeffNr]*=4.0*Pi/SphericalSamples.Size();
487
488                    // Initially, the Unradiated Transfer is the same as the Total Transfer,
489                    // exactly analogous to the direct lighting phase in traditional radiosity.
490                    Patch.SHCoeffs_UnradiatedTransfer[CoeffNr]=Patch.SHCoeffs_TotalTransfer[CoeffNr];
491                }
492            }
493    }
494}
495
496
497unsigned long BounceLighting(const CaSHLWorldT& CaSHLWorld, const char BLOCK_SIZE, double& StopUT, const bool AskForMore)
498{
499    const cf::SceneGraph::BspTreeNodeT& Map=CaSHLWorld.GetBspTree();
500
501    printf("\n%-50s %s\n", "*** PHASE II - performing bounce lighting ***", GetTimeSinceProgramStart());
502
503    unsigned long IterationCount =0;
504    unsigned long FullSearchCount=0;
505
506    while (true)
507    {
508        unsigned long Face_i=0;
509        unsigned long s_i   =0;
510        unsigned long t_i   =0;
511        double        BestUT=0;     // Best unradiated transfer amount found.
512
513        // Finde eine Face mit einem Patch mit einem großen "Unradiated Transfer" (nicht notwendigerweise die größte, damit es schnell geht).
514        unsigned long FaceNr;
515
516        for (FaceNr=0; FaceNr<Map.FaceChildren.Size(); FaceNr++)
517        {
518            // Achtung: Patches[FaceNr].Size kann auch 0 sein!
519            unsigned long                                 NrOfSamples=Patches[FaceNr].Size()<10 ? Patches[FaceNr].Size()/2 : 10;
520            const cf::SceneGraph::FaceNodeT::SHLMapInfoT& SMI        =Map.FaceChildren[FaceNr]->SHLMapInfo;
521
522            for (unsigned long SampleNr=0; SampleNr<NrOfSamples; SampleNr++)
523            {
524                unsigned long s=rand() % SMI.SizeS;     // Funktioniert nicht mehr gut wenn SMI.SizeS>RAND_MAX
525                unsigned long t=rand() % SMI.SizeT;     // Funktioniert nicht mehr gut wenn SMI.SizeT>RAND_MAX
526
527                s=(s/BLOCK_SIZE)*BLOCK_SIZE;
528                t=(t/BLOCK_SIZE)*BLOCK_SIZE;
529
530                unsigned long Count =0;
531                double        ThisUT=0.0;
532
533                for (char y=0; y<BLOCK_SIZE; y++)
534                    for (char x=0; x<BLOCK_SIZE; x++)
535                    {
536                        if (s+x+1>SMI.SizeS) continue;
537                        if (t+y+1>SMI.SizeT) continue;
538
539                        const PatchT& P_i=Patches[FaceNr][(t+y)*SMI.SizeS+(s+x)];
540                        if (!P_i.InsideFace) continue;
541
542                        for (unsigned long CoeffNr=0; CoeffNr<P_i.SHCoeffs_UnradiatedTransfer.Size(); CoeffNr++)
543                            ThisUT+=fabs(P_i.SHCoeffs_UnradiatedTransfer[CoeffNr]);
544
545                        Count++;
546                    }
547
548                if (!Count) continue;
549                ThisUT/=double(Count);
550
551                if (ThisUT>BestUT)
552                {
553                    Face_i=FaceNr;
554                    s_i   =s;
555                    t_i   =t;
556                    BestUT=ThisUT;
557                }
558            }
559        }
560
561        // Sollte der BestUT unter StopUT sein, wird hier nach einem besseren Wert gesucht.
562        // Beim erstbesseren Wert wird dieser genommen und abgebrochen, ansonsten gesucht bis zum Schluß.
563        // Wenn alle Faces nach dem besten Wert durchsucht werden sollen, muß "&& BestUT<StopUT" auskommentiert werden.
564        if (BestUT<StopUT)
565        {
566            FullSearchCount++;      // Zähle, wie oft wir alles abgesucht haben!
567
568            for (FaceNr=0; FaceNr<Map.FaceChildren.Size() && BestUT<StopUT; FaceNr++)
569            {
570                const cf::SceneGraph::FaceNodeT::SHLMapInfoT& SMI=Map.FaceChildren[FaceNr]->SHLMapInfo;
571
572                for (unsigned long s=0; s<SMI.SizeS; s++)
573                    for (unsigned long t=0; t<SMI.SizeT; t++)
574                    {
575                        const  PatchT& P=Patches[FaceNr][t*SMI.SizeS+s];
576
577                        double ThisUT=0.0;
578                        for (unsigned long CoeffNr=0; CoeffNr<P.SHCoeffs_UnradiatedTransfer.Size(); CoeffNr++)
579                            ThisUT+=fabs(P.SHCoeffs_UnradiatedTransfer[CoeffNr]);
580
581                        if (ThisUT>BestUT)
582                        {
583                            Face_i=FaceNr;
584                            s_i   =s;
585                            t_i   =t;
586                            BestUT=ThisUT;
587                        }
588                    }
589            }
590        }
591
592        printf("Iteration%6lu, BestUT %6.2f, F_i%5lu, FullSearch%4lu (%5.1f%%)\r", IterationCount, BestUT, Face_i, FullSearchCount, 100.0*float(FullSearchCount)/float(IterationCount+1));
593        fflush(stdout);
594
595        if (BestUT<StopUT)  // Es gab keinen besseren Wert mehr -- wir können also abbrechen!
596        {
597            printf("\n");
598            if (!AskForMore) break;
599
600            time_t StartTime=time(NULL);
601
602            printf("\nStopUT value %10.7f has been reached.\n", StopUT);
603            printf("Press 'y' to confirm exit and save current result,\n");
604            printf("or any other key to divide StopUT by 10 and continue lighting!\n");
605
606            char Key=_getch(); if (Key==0) _getch();
607
608            unsigned long TotalSec=(unsigned long)difftime(time(NULL), StartTime);
609            unsigned long Sec     =TotalSec % 60;
610            unsigned long Min     =(TotalSec/60) % 60;
611            unsigned long Hour    =TotalSec/3600;
612            printf("Length of break (waiting for your decision) was %2lu:%2lu:%2lu.\n", Hour, Min, Sec);
613
614            if (Key=='y') break;
615            StopUT/=10.0;
616        }
617
618        RadiateTransfer(CaSHLWorld, Face_i, s_i, t_i, BLOCK_SIZE);
619        IterationCount++;
620
621#ifdef _WIN32
622        // TODO: Ein (sinnvolles!) 'kbhit()' Äquivalent für Linux muß erst noch gefunden werden...
623        if (_kbhit())
624        {
625            char Key=_getch(); if (Key==0) _getch();
626
627            if (Key==' ')
628            {
629                printf("\nINTERRUPTED BY USER! Press 'y' to confirm exit and save current result,\n");
630                printf("or any other key to continue lighting!\n");
631
632                Key=_getch(); if (Key==0) _getch();
633                if (Key=='y') break;
634            }
635        }
636#endif
637    }
638
639    return IterationCount;
640}
641
642
643#include "Ward97.cpp"   // void ToneReproduction(const cf::SceneGraph::BspTreeNodeT& Map) { ... }
644
645
646void PostProcessBorders(const CaSHLWorldT& CaSHLWorld)
647{
648    const cf::SceneGraph::BspTreeNodeT& Map=CaSHLWorld.GetBspTree();
649
650    // An dieser Stelle haben wir nun quasi drei Sorten von Patches für eine Face:
651    // a) Patches, die 'InsideFace' liegen, und das vollständig.
652    // b) Patches, die 'InsideFace' liegen, aber nur teilweise (ihre Patch.Coord ist entsprechend verschoben!).
653    // c) Patches, die nicht 'InsideFace' liegen.
654    // Für Patch-Sorten a) und b) hat unser Algorithmus Patch.TotalEnergy-Werte berechnet.
655    // Unsere Aufgabe hier ist im wesentlichen das sinnvolle Ausfüllen der TotalEnergy-Werte von Patches der Sorte c).
656    // Dies ist notwendig, da die Patches von OpenGL beim Rendern bilinear interpoliert werden (2x2-Array Durchschnitt),
657    // und deswegen ohne weitere Maßnahmen schwarze Ränder bekämen.
658    printf("\n%-50s %s\n", "*** Post-Process Borders ***", GetTimeSinceProgramStart());
659
660
661    // ERSTER TEIL
662    // ***********
663
664    // Für alle Patches einer Face, die keinen SamplePoint innerhalb ihrer Face haben, ermittele ihren Wert aus dem
665    // Durchschnitt ihrer acht umliegenden Patches, sofern diese mit SamplePoints innerhalb der Face liegen.
666    // Diese Methode ist sehr einfach und schnell, da sie immer nur eine Face gleichzeitig betrachtet,
667    // die Nachbarumgebung hat keinen Einfluß.
668    // Dennoch ist diese Schleife ein guter Anfang, und war vorher sogar der *einzige* Nachbearbeitungsschritt!
669    // TODO: Ein "Weighted Average" wäre vielleicht besser, zumindest die "Ecken" des 3x3-Feldes könnten u.U.
670    // schwächer eingebracht werden (z.B. als ob es ein Kreis mit Radius 1,5 wäre). Nochmal überdenken!
671    // Damit könnte dann wohl auch die Spezialbehandlung der Ränder entfallen!?
672    for (unsigned long FaceNr=0; FaceNr<Map.FaceChildren.Size(); FaceNr++)
673    {
674        const cf::SceneGraph::FaceNodeT::SHLMapInfoT& SMI=Map.FaceChildren[FaceNr]->SHLMapInfo;
675
676        for (unsigned long t=0; t<SMI.SizeT; t++)
677            for (unsigned long s=0; s<SMI.SizeS; s++)
678            {
679                if (Patches[FaceNr][t*SMI.SizeS+s].InsideFace) continue;
680
681                const unsigned long SMI_SizeS=SMI.SizeS;
682                const unsigned long SMI_SizeT=SMI.SizeT;
683
684                ArrayT<double>& Cs=Patches[FaceNr][t*SMI.SizeS+s].SHCoeffs_TotalTransfer;
685
686                     if (s==0           && t>0 && t<SMI_SizeT-1) Cs=Patches[FaceNr][           t *SMI.SizeS+          1].SHCoeffs_TotalTransfer;    // linker  Rand (ohne Ecken)
687                else if (s==SMI_SizeS-1 && t>0 && t<SMI_SizeT-1) Cs=Patches[FaceNr][           t *SMI.SizeS+SMI.SizeS-2].SHCoeffs_TotalTransfer;    // rechter Rand (ohne Ecken)
688                else if (t==0           && s>0 && s<SMI_SizeS-1) Cs=Patches[FaceNr][           1 *SMI.SizeS+          s].SHCoeffs_TotalTransfer;    // oberer  Rand (ohne Ecken)
689                else if (t==SMI_SizeT-1 && s>0 && s<SMI_SizeS-1) Cs=Patches[FaceNr][(SMI.SizeT-2)*SMI.SizeS+          s].SHCoeffs_TotalTransfer;    // unterer Rand (ohne Ecken)
690                else // Alle anderen Patches (inkl. Ecken)
691                {
692                    const unsigned long NR_OF_SH_COEFFS=cf::SceneGraph::SHLMapManT::NrOfBands * cf::SceneGraph::SHLMapManT::NrOfBands;
693                    ArrayT<double>      Average;
694                    char                AverageCount=0;
695
696                    while (Average.Size()<NR_OF_SH_COEFFS) Average.PushBack(0.0);
697
698                    // Der Patch liegt in der Mitte eines 3x3-Feldes bei Koordinate (1,1).
699                    for (char y=0; y<=2; y++)
700                        for (char x=0; x<=2; x++)
701                        {
702                            if (x==1 && y==1) continue;                 // Nur die acht umliegenden Patches betrachten
703
704                            if (s==0           && x==0) continue;       // Linken  Rand beachten
705                            if (s==SMI_SizeS-1 && x==2) continue;       // Rechten Rand beachten
706                            if (t==0           && y==0) continue;       // Oberen  Rand beachten
707                            if (t==SMI_SizeT-1 && y==2) continue;       // Unteren Rand beachten
708
709                            if (!Patches[FaceNr][(t+y-1)*SMI.SizeS+(s+x-1)].InsideFace) continue;
710
711                            for (unsigned long CoeffNr=0; CoeffNr<NR_OF_SH_COEFFS; CoeffNr++)
712                                Average[CoeffNr]+=Patches[FaceNr][(t+y-1)*SMI.SizeS+(s+x-1)].SHCoeffs_TotalTransfer[CoeffNr];
713                            AverageCount++;
714                        }
715
716                    if (AverageCount)
717                        for (unsigned long CoeffNr=0; CoeffNr<NR_OF_SH_COEFFS; CoeffNr++)
718                            Cs[CoeffNr]=Average[CoeffNr]/double(AverageCount);
719                }
720            }
721    }
722
723
724    // ZWEITER TEIL
725    // ************
726
727    // Betrachte im nächsten Schritt Faces, die in einer gemeinsamen Ebene nahe beieinander liegen, und versuche,
728    // den "Übergang" zu verbessern. Der vorangegangene erste Schritt eliminiert zwar Fehlfarben an den Rändern,
729    // die OpenGL's bilinear Filtering ansonsten ins Spiel gebracht hätte, an Stellen mit hohen Kontrasten
730    // ("scharfe" Schatten usw.) sieht man aber unbeabsichtigte, harte Übergänge an den Kanten solcher Faces.
731    // Der folgende Code eliminiert solche Sprünge nun größtenteils, indem er auch die Patches der anderen Faces betrachtet.
732    // Damit werden für die Ränder die "realen" Berechnungsergebnisse eingebracht, nicht einfach nur eigene Mittelwerte.
733    // Der folgende Code könnte algorithmisch effizienter geschrieben sein (z.B. Vorausberechnen von wiederkehrenden
734    // Werten, statt diese jedesmal in einer Schleife neu zu berechnen), aber die praktische Laufzeit ist akzeptabel.
735    //
736    // Vorgehensweise:
737    // Zu jedem Patch P1 jeder Face suchen wir alle Patches Pi heraus, die irgendetwas zu P1 beitragen könnten.
738    // Dazu müssen sich P1 und die Pi insbesondere in der selben Ebene überlappen.
739    // Wegen der global einheitlichen Ausrichtung aller Patches in einer Ebene sind Überlappungen immer "ganz oder gar nicht".
740    // Jeder Patch wird klassifiziert, ob er vollständig in seiner Face liegt (INNER), auf dem Rand (PARTIAL),
741    // oder vollständig außerhalb (OUTER). Somit ergeben sich folgende Möglichkeiten:
742    //
743    // P1 \ Pi |  INNER  | PARTIAL |  OUTER
744    // --------+---------+---------+---------
745    // INNER   |    11   |    12   |    13
746    // PARTIAL |    21   |    22   |    23
747    // OUTER   |    31   |    32   |    33
748    //
749    // Die Fälle P1==INNER (11, 12, 13) interessieren uns nicht, da wir für solche P1 einwandfreie Berechnungsergebnisse haben,
750    // und es keinen Grund gibt, mit anderen Werten (von Pi's) daran etwas herumzufummeln.
751    // Die Fälle Pi==OUTER (13, 23, 33) können wir auch direkt überspringen, weil es keinen Sinn macht, P1 irgendwie mit einem
752    // anderen OUTER-Patch zu modifizieren - dieser hat schließlich selbst keinen verwendbaren Wert.
753    // Der Fall 21 kann niemals vorkommen, denn ein Patch kann nicht vollständig in einer Face liegen, und teilweise in einer anderen
754    // (Faces überlappen sich nicht!). Es müssen also nur die Fälle 22, 31 und 32 betrachtet werden.
755    //
756    // In allen drei verbleibenden Fällen gilt, daß ein Pi nur dann beitragen darf, wenn er damit kein "Light Bleeding" verursacht.
757    // "Light Bleeding" ist die Beeinflussung von P1 durch andere Patches Pi, deren Faces sich zwar nahe, aber in Wirklichkeit z.B.
758    // durch eine dünne "Wand" getrennt sind, oder die Patches liegen "um die Ecke".
759    // Um das "Light Bleeding" Problem zu minimieren, erlauben wir die Beeinflussung von P1 durch ein Pi nur dann, wenn ein Punkt,
760    // der garantiert in der Face von P1 und möglichst nahe bei P1 liegt, die Pi.Coord "sehen" kann.
761    // Analytisch mag das nicht 100%ig korrekt sein (???), ergibt in der Praxis aber einwandfreie Ergebnisse!
762    //
763    // Fall 31 läßt sich nun leicht abhaken: Max. ein Pi ist möglich, und damit erhalten wir P1=Pi.
764    // Für Fall 32 erhalten wir P1="area-weighted average of the involved Pi".
765    //
766    // Fall 22 ist etwas schwieriger: Im Idealfall würden wir schreiben: P1="area-weighted average of the involved Pi and P1 itself".
767    // Dummerweise verändern wir damit P1, welches aber wiederum später eines der jetzigen Pi beeinflussen wird, wenn dieses Pi an der
768    // Reihe ist. Dann müßte P1 aber seinen Original-Wert haben, nicht den, den wir gerade dabei sind hineinzuschreiben.
769    // (Das sieht man auch daran, daß Fall 22 in obiger Matrix auf der Hauptdiagonalen liegt. Die beiden Fälle 31 und 32 (und 21) liegen
770    // im unteren linken Dreieck, während das rechte obere Dreieck (12, 13, 23) "inaktiv" ist. Deshalb sind 31 und 32 unproblematisch.)
771    // Lösung: Schreibe den gefundenen Wert nicht nur nach P1, sondern auch in ALLE beteiligten Pi. Das würde das Problem vollständig(!)
772    // lösen, denn spätere Mittelwertbildungen wären zwar redundant, kämen aber zu dem selben (korrekten) Ergebnis.
773    // Leider macht das "Light Bleeding"-Problem einen Strich durch die Rechnung! Wenn z.B. Pi={ P2, P3 }, dann könnte P1 von P2 beeinflußt
774    // werden, P3 aber wegen "Light Bleeding" vom Einfluß auf P1 ausgeschlossen werden. P2 könnte später aber sehr wohl von P1 UND P3 beeinflußt
775    // werden! P2 drücken wir aber nach obiger "Lösung" aber schon mal einen Mittelwert auf, und insgesamt kommt es zum falschen Ergebnis.
776    // INSGESAMT wäre die einzige WIRKLICHE Lösung, ein Backup aller Patches anzulegen, und alle Mittelwerte stets daraus zu bilden.
777    // Das verdoppelt aber sofort den Speicherbedarf, der ohnehin schon bei mehreren hundert MB liegt. Außerdem ist der bei
778    // der ersten "Lösung" entstehende Fehler wohl sehr selten und sehr klein. Konsequenz: Wir ignorieren ihn, und wenden die erste Lösung an!
779    //
780    // Zuletzt ist noch zu beachten, daß das Ausschließen von Patches wg. Light Bleeding dazu führen kann, daß die Summe der Gewichte beim
781    // weighted average < 1.0 wird. In diesen Fällen müssen die Gewichte "renormalisiert" werden.
782    // Zu diesen Ausführungen siehe auch die Skizze im Cafu Tech-Archive vom 10.12.2003!
783    const double  PATCH_SIZE          =cf::SceneGraph::FaceNodeT::SHLMapInfoT::PatchSize;
784    unsigned long PatchesWorkedOnCount=0;
785
786    for (unsigned long Face1Nr=0; Face1Nr<Map.FaceChildren.Size(); Face1Nr++)
787    {
788        const Polygon3T<double>& Face1=Map.FaceChildren[Face1Nr]->Polygon;
789        const cf::SceneGraph::FaceNodeT::LightMapInfoT& Face1_SMI=Map.FaceChildren[Face1Nr]->LightMapInfo;
790        ArrayT<unsigned long> NearFaces;
791
792        printf("%5.1f%%\r", (double)Face1Nr/Map.FaceChildren.Size()*100.0);
793        fflush(stdout);
794
795        // Bilde zuerst eine Liste (von Indizes) von Faces, die Face1 "nahe" sind.
796        for (unsigned long Face2Nr=0; Face2Nr<Map.FaceChildren.Size(); Face2Nr++)
797        {
798            const Polygon3T<double>& Face2=Map.FaceChildren[Face2Nr]->Polygon;
799
800            // Wir wollen nicht gegen uns selbst testen!
801            if (Face1Nr==Face2Nr) continue;
802
803            // Nur Faces in der gleichen Ebene mit gleicher Orientierung durchgehen lassen!
804            if (Face1.WhatSide(Face2.Plane, MapT::RoundEpsilon)!=Polygon3T<double>::InIdentical) continue;
805
806            // Faces, die zu weit voneinander entfernt liegen, brauchen nicht weiter betrachtet zu werden!
807            const VectorT BorderPadding(PATCH_SIZE, PATCH_SIZE, PATCH_SIZE);
808
809            BoundingBox3T<double> BB1(Face1.Vertices); BB1.Min-=BorderPadding; BB1.Max+=BorderPadding;
810            BoundingBox3T<double> BB2(Face2.Vertices); BB2.Min-=BorderPadding; BB2.Max+=BorderPadding;
811
812            if (!BB1.Intersects(BB2)) continue;
813
814            // Ein Sanity-Check, wg. Rundungsfehlern.
815            // Eigentlich müssen beide Planes nämlich exakt gleich sein!
816            if (Face1.Plane.Normal.x!=Face2.Plane.Normal.x) printf("WARNING: Face1.Plane.Normal.x!=Face2.Plane.Normal.x (%.15f != %.15f)\n", Face1.Plane.Normal.x, Face2.Plane.Normal.x);
817            if (Face1.Plane.Normal.y!=Face2.Plane.Normal.y) printf("WARNING: Face1.Plane.Normal.y!=Face2.Plane.Normal.y (%.15f != %.15f)\n", Face1.Plane.Normal.y, Face2.Plane.Normal.y);
818            if (Face1.Plane.Normal.z!=Face2.Plane.Normal.z) printf("WARNING: Face1.Plane.Normal.z!=Face2.Plane.Normal.z (%.15f != %.15f)\n", Face1.Plane.Normal.z, Face2.Plane.Normal.z);
819            if (Face1.Plane.Dist    !=Face2.Plane.Dist    ) printf("WARNING: Face1.Plane.Dist    !=Face2.Plane.Dist     (%.15f != %.15f)\n", Face1.Plane.Dist    , Face2.Plane.Dist    );
820
821            // Diese Face kommt in Betracht, speichere ihre Nummer.
822            NearFaces.PushBack(Face2Nr);
823        }
824
825        // Bereite nun das PatchPoly vor. Unten werden dann nur noch die 4 Vertices ausgefüllt.
826        // WICHTIG: Dieses PatchPoly ist für zwei sich überlappende Patches EXAKT IDENTISCH, da alle Patches gleich ausgerichtet sind!
827        // Der folgende Code ist SEHR ähnlich zu dem Code in InitializePatches() (Init2.cpp)!
828
829        // Bestimme die Spannvektoren der gemeinsamen Ebene.
830        // (Face1 und die Faces2 unten liegen in einer *gemeinsamen* Ebene!)
831        VectorT U;
832        VectorT V;
833
834        Face1.Plane.GetSpanVectors(U, V);
835
836        // Finde SmallestU und SmallestV.
837        double Face1_SmallestU=dot(Face1.Vertices[0], U);
838        double Face1_SmallestV=dot(Face1.Vertices[0], V);
839
840        for (unsigned long VertexNr=1; VertexNr<Face1.Vertices.Size(); VertexNr++)
841        {
842            double u=dot(Face1.Vertices[VertexNr], U);
843            double v=dot(Face1.Vertices[VertexNr], V);
844
845            if (u<Face1_SmallestU) Face1_SmallestU=u;
846            if (v<Face1_SmallestV) Face1_SmallestV=v;
847        }
848
849        Face1_SmallestU=floor(Face1_SmallestU/PATCH_SIZE);
850        Face1_SmallestV=floor(Face1_SmallestV/PATCH_SIZE);
851
852        const VectorT UV_Origin=scale(Face1.Plane.Normal, Face1.Plane.Dist);
853        const VectorT Safety   =scale(Face1.Plane.Normal, 0.1);
854
855        Polygon3T<double> PatchPoly;
856
857        PatchPoly.Plane=dot(Face1.Plane.Normal, cross(U, V))<0 ? Face1.Plane : Face1.Plane.GetMirror();
858        PatchPoly.Vertices.PushBackEmpty(4);
859
860        // Betrachte nun alle Patches von Face1.
861        for (unsigned long t1=0; t1<Face1_SMI.SizeT; t1++)
862            for (unsigned long s1=0; s1<Face1_SMI.SizeS; s1++)
863            {
864                PatchT& Patch1=Patches[Face1Nr][t1*Face1_SMI.SizeS+s1];
865
866                // Rekonstruiere das Polygon zu Patch1.
867                PatchPoly.Vertices[0]=UV_Origin+scale(U, (Face1_SmallestU+s1-1.0)*PATCH_SIZE)+scale(V, (Face1_SmallestV+t1-1.0)*PATCH_SIZE);
868                PatchPoly.Vertices[1]=UV_Origin+scale(U, (Face1_SmallestU+s1    )*PATCH_SIZE)+scale(V, (Face1_SmallestV+t1-1.0)*PATCH_SIZE);
869                PatchPoly.Vertices[2]=UV_Origin+scale(U, (Face1_SmallestU+s1    )*PATCH_SIZE)+scale(V, (Face1_SmallestV+t1    )*PATCH_SIZE);
870                PatchPoly.Vertices[3]=UV_Origin+scale(U, (Face1_SmallestU+s1-1.0)*PATCH_SIZE)+scale(V, (Face1_SmallestV+t1    )*PATCH_SIZE);
871
872                // Klassifizierung: Falls Patch1 *vollständig in* seiner Face liegt, haben wir dafür einen
873                // einwandfreien Berechnungswert, und wir wollen daran nicht rumfummeln (Fälle 11, 12, 13)!
874                // Ob Patch1 PARTIAL oder OUTER ist, wird weiter unten genauer klassifiziert.
875                if (Face1.Encloses(PatchPoly, true, MapT::RoundEpsilon)) continue;
876
877
878                // Den Mittelpunkt des PatchPoly bestimmen, inkl. "Safety".
879                const VectorT PatchPoly_Center=scale(PatchPoly.Vertices[0]+PatchPoly.Vertices[1]+PatchPoly.Vertices[2]+PatchPoly.Vertices[3], 0.25)+Safety;
880
881                // Suche einen Punkt *IN* Face1 heraus, der "nahe" bei Patch1 liegt. Wird unten benötigt.
882                double  MinDistance=3.0*PATCH_SIZE;
883                VectorT InnerPointCloseToPatch1;
884
885                for (unsigned long PatchNr=0; PatchNr<Patches[Face1Nr].Size(); PatchNr++)
886                {
887                    const PatchT& TempPatch=Patches[Face1Nr][PatchNr];
888
889                    // 'TempPatch' darf auch ruhig 'Patch1' sein, da 'Patch1.Coord' durchaus ein Punkt in Face1 ist, der "nahe" bei Patch1 liegt!
890                    if (!TempPatch.InsideFace) continue;
891
892                    double Distance=length(TempPatch.Coord-PatchPoly_Center);
893
894                    if (Distance<MinDistance)
895                    {
896                        MinDistance            =Distance;
897                        InnerPointCloseToPatch1=TempPatch.Coord;
898                    }
899                }
900
901                // Wurde auch etwas in der Nähe gefunden?
902                if (MinDistance==3.0*PATCH_SIZE) continue;
903
904
905                // Betrachte nun die umliegenden Faces, um herauszufinden, ob sie Patches enthalten,
906                // die zu "unserem" Patch etwas beitragen könnten.
907                ArrayT<PatchT*> ContributingPatches;
908                ArrayT<double > ContributingPatchesInFace;  // Mit wieviel % seiner Fläche liegt der Patch in seiner Face?
909
910                for (unsigned long NearNr=0; NearNr<NearFaces.Size(); NearNr++)
911                {
912                    const Polygon3T<double>& Face2=Map.FaceChildren[NearFaces[NearNr]]->Polygon;
913                    const cf::SceneGraph::FaceNodeT::LightMapInfoT& Face2_SMI=Map.FaceChildren[NearFaces[NearNr]]->LightMapInfo;
914
915                    double Face2_SmallestU=dot(Face2.Vertices[0], U);
916                    double Face2_SmallestV=dot(Face2.Vertices[0], V);
917
918                    for (unsigned long VertexNr=1; VertexNr<Face2.Vertices.Size(); VertexNr++)
919                    {
920                        double u=dot(Face2.Vertices[VertexNr], U);
921                        double v=dot(Face2.Vertices[VertexNr], V);
922
923                        if (u<Face2_SmallestU) Face2_SmallestU=u;
924                        if (v<Face2_SmallestV) Face2_SmallestV=v;
925                    }
926
927                    Face2_SmallestU=floor(Face2_SmallestU/PATCH_SIZE);
928                    Face2_SmallestV=floor(Face2_SmallestV/PATCH_SIZE);
929
930                    // Gehe die Patches von Face2 durch
931                    for (unsigned long t2=0; t2<Face2_SMI.SizeT; t2++)
932                        for (unsigned long s2=0; s2<Face2_SMI.SizeS; s2++)
933                            // Überlappen sich die Patches? Beachte: Wenn, dann ist es eine *exakte* Überlappung, wegen gleichem Patch-Alignment!
934                            if ((unsigned long)(Face1_SmallestU)+s1==(unsigned long)(Face2_SmallestU)+s2 &&
935                                (unsigned long)(Face1_SmallestV)+t1==(unsigned long)(Face2_SmallestV)+t2)
936                            {
937                                PatchT& Patch2=Patches[NearFaces[NearNr]][t2*Face2_SMI.SizeS+s2];
938
939                                // Nur "äußere" Patches von Face1 mit "inneren" Patches von Face1 korrigieren!
940                                if (!Patch2.InsideFace) continue;
941
942                                // Avoid "Light Bleeding" by this simple visibility test.
943                                if (CaSHLWorld.TraceRay(InnerPointCloseToPatch1, Patch2.Coord-InnerPointCloseToPatch1)<1.0) continue;
944
945
946                                ArrayT< Polygon3T<double> > NewPolygons;
947
948                                PatchPoly.GetChoppedUpAlong(Face2, MapT::RoundEpsilon, NewPolygons);
949                                if (NewPolygons.Size()==0) Error("PolygonChopUp failed in PostProcessBorders().");
950
951                                // Mit wieviel % seiner Fläche liegt PatchPoly in Face2?
952                                const double AreaRatio=NewPolygons[NewPolygons.Size()-1].GetArea()/PatchPoly.GetArea();
953
954                                ContributingPatches      .PushBack(&Patch2);
955                                ContributingPatchesInFace.PushBack(AreaRatio);
956
957
958                                // Directly continue with the next face.
959                                t2=Face2_SMI.SizeT;
960                                break;
961                            }
962                }
963
964
965                if (ContributingPatches.Size()==0) continue;
966
967                const unsigned long NR_OF_SH_COEFFS=cf::SceneGraph::SHLMapManT::NrOfBands * cf::SceneGraph::SHLMapManT::NrOfBands;
968
969
970                // Klassifiziere Patch1 weiter:
971                // Daß er INNER ist, haben wir oben schon ausgeschlossen.
972                // Er ist PARTIAL, wenn Patch1.InsideFace==true, sonst OUTER.
973                if (Patch1.InsideFace)
974                {
975                    // Patch1 is "PARTIAL" (partially inside of Face1) - dies ist also Fall 22.
976                    // Note that in case 22, Patch1 is contributing to itself, thus add it to the ContributingPatches and ContributingPatchesInFace arrays.
977                    ArrayT< Polygon3T<double> > NewPolygons;
978
979                    PatchPoly.GetChoppedUpAlong(Face1, MapT::RoundEpsilon, NewPolygons);
980                    if (NewPolygons.Size()==0) Error("PolygonChopUp failed in PostProcessBorders().");
981
982                    // Mit wieviel % seiner Fläche liegt PatchPoly in Face1?
983                    const double AreaRatio=NewPolygons[NewPolygons.Size()-1].GetArea()/PatchPoly.GetArea();
984
985                    ContributingPatches      .PushBack(&Patch1);
986                    ContributingPatchesInFace.PushBack(AreaRatio);
987                }
988
989
990                // Re-normalize the contribution percentages of the contributing patches.
991                // This is necessary because light-bleeding might omit patches that otherwise had contributed.
992                unsigned long CPNr;
993                double        Sum=0.0;
994
995                for (CPNr=0; CPNr<ContributingPatchesInFace.Size(); CPNr++) Sum+=ContributingPatchesInFace[CPNr];
996                for (CPNr=0; CPNr<ContributingPatchesInFace.Size(); CPNr++) ContributingPatchesInFace[CPNr]/=Sum;
997
998
999                // Compute the weighted average.
1000                ArrayT<double> ResultCoeffs;
1001
1002                unsigned long CoeffNr;
1003                for (CoeffNr=0; CoeffNr<NR_OF_SH_COEFFS; CoeffNr++) ResultCoeffs.PushBack(0.0);
1004
1005                for (CPNr=0; CPNr<ContributingPatches.Size(); CPNr++)
1006                    for (CoeffNr=0; CoeffNr<NR_OF_SH_COEFFS; CoeffNr++)
1007                        ResultCoeffs[CoeffNr]+=ContributingPatches[CPNr]->SHCoeffs_TotalTransfer[CoeffNr]*ContributingPatchesInFace[CPNr];
1008
1009                if (Patch1.InsideFace)
1010                {
1011                    for (CPNr=0; CPNr<ContributingPatches.Size(); CPNr++)
1012                        ContributingPatches[CPNr]->SHCoeffs_TotalTransfer=ResultCoeffs;     // Case 22.
1013                }
1014                else Patch1.SHCoeffs_TotalTransfer=ResultCoeffs;    // Case 31 or 32.
1015
1016                PatchesWorkedOnCount++;
1017            }
1018    }
1019
1020    printf("Borders completed. %lu patches modified in 2nd part.\n", PatchesWorkedOnCount);
1021}
1022
1023
1024void Usage()
1025{
1026    printf("\n");
1027    printf("USAGE: CaSHL WorldName [OPTIONS]\n");
1028    printf("\n");
1029    printf("-Bands n       Number of SH bands (yielding n*n SH coeffs).\n");
1030    printf("               n must be in range 0..10, default is %u.\n", cf::SceneGraph::SHLMapManT::NrOfBands);
1031    printf("-NrOfSamples n Approx. number of SH samples (I'll determine the exact number).\n");
1032    printf("               n must be in range 20..100000, default is 10000.\n");
1033    printf("-SkipBL        Skip bounce lighting. Without BL, what I compute is analogous to\n");
1034    printf("               Robin Greens \"2 Shadowed Diffuse Transfer\". With BL, it is\n");
1035    printf("               analogous to \"3 Diffuse Interreflected Transfer\" (p. 31).\n");
1036    printf("-BlockSize n   Radiative block size for faster bounce lighting.\n");
1037    printf("               n must be in range 1..8, default is 3.\n");
1038    printf("-StopUT f      Stop value for unradiated transfer. 0 < f <= 10, default is 0.1.\n");
1039    printf("-AskForMore    Asks for a new StopUT value when the old one has been reached.\n");
1040    printf("-NoFullVis     Do not precompute 'full vis' acceleration information.\n");
1041    printf("               Makes initialization much faster, but lighting a bit slower.\n");
1042    printf("-fast          Same as \"-BlockSize 5 -NoFullVis\".\n");
1043    printf("-Reps n        Number of representative SH vectors used for compression.\n");
1044    printf("               Default is %u. 0 means no compression.\n", cf::SceneGraph::SHLMapManT::NrOfRepres);
1045    printf("\n");
1046    printf("\n");
1047    printf("EXAMPLES:\n");
1048    printf("\n");
1049    printf("CaSHL WorldName -AskForMore\n");
1050    printf("    I'll start with the default parameters, light the world WorldName and\n");
1051    printf("    finally ask you what to do when the StopUT value has been reached.\n");
1052    printf("\n");
1053    printf("CaSHL WorldName -StopUT 0.1\n");
1054    printf("    Most worlds of the Cafu demo release are lit with these switches.\n");
1055    printf("\n");
1056    printf("CaSHL WorldName -BlockSize 1 -StopUT 0.1\n");
1057    printf("    This is ideal for batch file processing: WorldName is lit without further\n");
1058    printf("    user questioning and I'll terminate as soon as StopUT has been reached.\n");
1059    printf("    Note that BlockSize and StopUT are set for high-quality lighting here.\n");
1060    printf("\n");
1061    printf("CaSHL WorldName -fast\n");
1062    printf("CaSHL WorldName -BlockSize 5 -NoFullVis\n");
1063    printf("    Fast and ugly lighting, intended for quick tests during world development.\n");
1064    exit(1);
1065}
1066
1067
1068static void WriteLogFileEntry(const char* WorldPathName, double StopUT, char BlockSize, unsigned long IterationCount)
1069{
1070    char          DateTime [256]="unknown";
1071    char          HostName [256]="unknown";
1072    char          WorldName[256]="unknown";
1073    time_t        Time          =time(NULL);
1074    unsigned long RunningSec    =(unsigned long)difftime(Time, ProgramStartTime);
1075    FILE*         LogFile       =fopen("CaSHL.log", "a");
1076
1077    if (!LogFile) return;
1078
1079    strftime(DateTime, 256, "%d.%m.%Y %H:%M", localtime(&Time));
1080    DateTime[255]=0;
1081
1082#ifdef _WIN32
1083    unsigned long Dummy=256;
1084    if (!GetComputerName(HostName, &Dummy)) sprintf(HostName, "unknown (look-up failed).");
1085#else
1086    // This function also works on Windows, but sadly requires calls to 'WSAStartup()' and 'WSACleanup()'.
1087    if (gethostname(HostName, 256)) sprintf(HostName, "unknown (look-up failed).");
1088#endif
1089    HostName[255]=0;
1090
1091    if (WorldPathName)
1092    {
1093        // Dateinamen abtrennen (mit Extension).
1094        size_t i=strlen(WorldPathName);
1095
1096        while (i>0 && WorldPathName[i-1]!='/' && WorldPathName[i-1]!='\\') i--;
1097        strncpy(WorldName, WorldPathName+i, 256);
1098        WorldName[255]=0;
1099
1100        // Extension abtrennen.
1101        i=strlen(WorldName);
1102
1103        while (i>0 && WorldName[i-1]!='.') i--;
1104        if (i>0) WorldName[i-1]=0;
1105    }
1106
1107    // Date, Time, WorldName, TimeForCompletion on [HostName]
1108    fprintf(LogFile, "%-16s %-16s%3lu:%02lu:%02lu [%-16s]%8.5f %ux%u%8lu\n", DateTime, WorldName, RunningSec/3600, (RunningSec/60) % 60, RunningSec % 60, HostName, StopUT, BlockSize, BlockSize, IterationCount);
1109    fclose(LogFile);
1110}
1111
1112
1113int main(int ArgC, const char* ArgV[])
1114{
1115    struct CaSHLOptionsT
1116    {
1117        char          BlockSize;
1118        double        StopUT;
1119        bool          AskForMore;
1120        bool          UseFullVis;
1121        unsigned long SqrtNrOfSamples;
1122        bool          SkipBL;
1123
1124        CaSHLOptionsT() : BlockSize(3), StopUT(0.1), AskForMore(false), UseFullVis(true), SqrtNrOfSamples(100), SkipBL(false) {}
1125    } CaSHLOptions;
1126
1127    cf::SceneGraph::SHLMapManT::NrOfBands=4;
1128
1129
1130    // Init screen
1131    printf("\n*** Cafu SHL Utility, Version 01 (%s) ***\n\n\n", __DATE__);
1132#if USE_NORMALMAPS
1133#if !defined(_MSC_VER)
1134    printf("This version of CaSHL takes the normal-maps of surfaces into account!\n");
1135
1136    // As we need access to the normal-maps, do a quick check if "./Textures" is a proper directory.
1137    DIR* TempDir=opendir("./Textures");
1138
1139    if (TempDir==NULL)
1140    {
1141        printf("I'm sorry, but for taking normal-maps into account, I need to be able to find\n");
1142        printf("them. I thus looked for the \"./Textures\" directory, but wasn't able to find it.\n");
1143        printf("Please cd into the \"Games/DeathMatch\" directory and run me again from there!\n");
1144
1145        Error("Texture directory not found.\n");
1146    }
1147    closedir(TempDir);
1148#endif
1149#endif
1150
1151    // Initialize the FileMan by mounting the default file system.
1152    // Note that specifying "./" (instead of "") as the file system description effectively prevents the use of
1153    // absolute paths like "D:\abc\someDir\someFile.xy" or "/usr/bin/xy". This however should be fine for this application.
1154    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_LOCAL_PATH, "./", "");
1155    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/TechDemo.zip", "Games/DeathMatch/Textures/TechDemo/", "Ca3DE");
1156    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/SkyDomes.zip", "Games/DeathMatch/Textures/SkyDomes/", "Ca3DE");
1157
1158    if (ArgC<2) Usage();
1159
1160    // Process command line
1161    for (int CurrentArg=2; CurrentArg<ArgC; CurrentArg++)
1162    {
1163        if (!_stricmp(ArgV[CurrentArg], "-Bands"))
1164        {
1165            if (CurrentArg+1==ArgC) Error("I can't find a number after \"-Bands\"!");
1166            CurrentArg++;
1167
1168            cf::SceneGraph::SHLMapManT::NrOfBands=atoi(ArgV[CurrentArg]);
1169            if (cf::SceneGraph::SHLMapManT::NrOfBands>10) Error("Bands must be in range 0..10.");
1170        }
1171        else if (!_stricmp(ArgV[CurrentArg], "-NrOfSamples"))
1172        {
1173            if (CurrentArg+1==ArgC) Error("I can't find a number after \"-NrOfSamples\"!");
1174            CurrentArg++;
1175
1176            const int NrOfSamples=atoi(ArgV[CurrentArg]);
1177            if (NrOfSamples<20 || NrOfSamples>100000) Error("NrOfSamples must be in range 20..100000.");
1178
1179            CaSHLOptions.SqrtNrOfSamples=(unsigned long)(sqrt(double(NrOfSamples))+0.5);
1180        }
1181        else if (!_stricmp(ArgV[CurrentArg], "-SkipBL"))
1182        {
1183            CaSHLOptions.SkipBL=true;
1184        }
1185        else if (!_stricmp(ArgV[CurrentArg], "-BlockSize"))
1186        {
1187            if (CurrentArg+1==ArgC) Error("I can't find a number after \"-BlockSize\"!");
1188            CurrentArg++;
1189            CaSHLOptions.BlockSize=atoi(ArgV[CurrentArg]);
1190            if (CaSHLOptions.BlockSize<1 || CaSHLOptions.BlockSize>8) Error("BlockSize must be in range 1..8.");
1191        }
1192        else if (!_stricmp(ArgV[CurrentArg], "-StopUT"))
1193        {
1194            if (CurrentArg+1==ArgC) Error("I can't find a number after \"-StopUT\"!");
1195            CurrentArg++;
1196            CaSHLOptions.StopUT=atof(ArgV[CurrentArg]);
1197            if (CaSHLOptions.StopUT<=0.0 || CaSHLOptions.StopUT>10.0) Error("StopUT must be in ]0, 10].");
1198        }
1199        else if (!_stricmp(ArgV[CurrentArg], "-AskForMore"))
1200        {
1201            CaSHLOptions.AskForMore=true;
1202        }
1203        else if (!_stricmp(ArgV[CurrentArg], "-NoFullVis"))
1204        {
1205            CaSHLOptions.UseFullVis=false;
1206        }
1207        else if (!_stricmp(ArgV[CurrentArg], "-fast"))
1208        {
1209            CaSHLOptions.BlockSize=5;
1210            CaSHLOptions.UseFullVis=false;
1211        }
1212        else if (!_stricmp(ArgV[CurrentArg], "-Reps"))
1213        {
1214            if (CurrentArg+1==ArgC) Error("I can't find a number after \"-Reps\"!");
1215            CurrentArg++;
1216
1217            cf::SceneGraph::SHLMapManT::NrOfRepres=atoi(ArgV[CurrentArg]);
1218            if (cf::SceneGraph::SHLMapManT::NrOfRepres>65536) Error("Reps must be in range 0..65536.");
1219        }
1220        else if (ArgV[CurrentArg][0]==0)
1221        {
1222            // The argument is "", the empty string.
1223            // This can happen under Linux, when CaSHL is called via wxExecute() with white-space trailing the command string.
1224        }
1225        else
1226        {
1227            printf("\nSorry, I don't know what \"%s\" means.\n", ArgV[CurrentArg]);
1228            Usage();
1229        }
1230    }
1231
1232
1233    std::string GameDirectory=ArgV[1];
1234
1235    // Determine the game directory, cleverly assuming that the destination file is in "Worlds".
1236    {
1237        // Strip the file name and extention off.
1238        size_t i=GameDirectory.find_last_of("/\\");
1239
1240        GameDirectory=GameDirectory.substr(0, i==std::string::npos ? 0 : i)+"/..";
1241    }
1242
1243
1244    // Setup the global MaterialManager pointer.
1245    static MaterialManagerImplT MatManImpl;
1246
1247    MaterialManager=&MatManImpl;
1248
1249    if (MaterialManager->RegisterMaterialScriptsInDir(GameDirectory+"/Materials", GameDirectory+"/").Size()==0)
1250    {
1251        printf("\nNo materials found in scripts in \"%s/Materials\".\n", GameDirectory.c_str());
1252        printf("No materials found.\n\n");
1253        Usage();
1254    }
1255
1256
1257    try
1258    {
1259        printf("Loading World '%s'.\n", ArgV[1]);
1260
1261        const char          Save_NrOfBands=cf::SceneGraph::SHLMapManT::NrOfBands;
1262        const unsigned long Save_NrOfReps =cf::SceneGraph::SHLMapManT::NrOfRepres;
1263        ModelManagerT ModelMan;
1264        CaSHLWorldT   CaSHLWorld(ArgV[1], ModelMan);
1265        cf::SceneGraph::SHLMapManT::NrOfBands =Save_NrOfBands;
1266        cf::SceneGraph::SHLMapManT::NrOfRepres=Save_NrOfReps;
1267
1268        // Print out options summary
1269        printf("\n");
1270        printf("- cf::SceneGraph::SHLMapManT::NrOfBands is %u (yielding %u^2==%lu SH coeffs).\n", cf::SceneGraph::SHLMapManT::NrOfBands, cf::SceneGraph::SHLMapManT::NrOfBands, (unsigned long)(cf::SceneGraph::SHLMapManT::NrOfBands)*(unsigned long)(cf::SceneGraph::SHLMapManT::NrOfBands));
1271        printf("- Number of SH samples is %lu.\n", CaSHLOptions.SqrtNrOfSamples*CaSHLOptions.SqrtNrOfSamples);
1272        printf("- I will %s the bounce lighting phase.\n", CaSHLOptions.SkipBL ? "SKIP" : "NOT skip");
1273        printf("- BlockSize is %ux%u.\n", CaSHLOptions.BlockSize, CaSHLOptions.BlockSize);
1274        printf("- StopUT    is %.3f.\n", CaSHLOptions.StopUT);
1275        printf("- I will %s you for more.\n", CaSHLOptions.AskForMore ? "ASK" : "NOT ask");
1276        printf("- I will %s the 'full vis' acceleration.\n", CaSHLOptions.UseFullVis ? "USE" : "NOT use");
1277        printf("- cf::SceneGraph::SHLMapManT::NrOfRepres is %u (compression is %s).\n", cf::SceneGraph::SHLMapManT::NrOfRepres, cf::SceneGraph::SHLMapManT::NrOfRepres>0 ? "ON" : "OFF");
1278        if (cf::SceneGraph::SHLMapManT::NrOfRepres>0)
1279        {
1280            const unsigned long NR_OF_SH_COEFFS    =cf::SceneGraph::SHLMapManT::NrOfBands * cf::SceneGraph::SHLMapManT::NrOfBands;
1281            const unsigned long NrOfColumns        =(cf::SceneGraph::SHLMapManT::NrOfRepres+255)/256;   // =ceil(double(cf::SceneGraph::SHLMapManT::NrOfRepres)/256);
1282            const unsigned long NrOfPixelsPerVector=(NR_OF_SH_COEFFS+3)/4;
1283
1284            unsigned long Width=1; while (Width<NrOfColumns*NrOfPixelsPerVector) Width*=2;
1285
1286            printf("  The look-up texture of representatives in the engine will have size %lu x 256 (%.1f%% unused).\n", Width, 100.0-100.0*double(cf::SceneGraph::SHLMapManT::NrOfRepres*NrOfPixelsPerVector)/double(256*Width));
1287        }
1288
1289        // Initialize
1290        InitializeFacePVSMatrix(CaSHLWorld, CaSHLOptions.UseFullVis);   // Init1.cpp
1291        InitializePatches(CaSHLWorld.GetBspTree());                     // Init2.cpp
1292
1293        // Perform lighting.
1294        DirectLighting(CaSHLWorld, CaSHLOptions.SqrtNrOfSamples);
1295        unsigned long IterationCount=CaSHLOptions.SkipBL ? 0 : BounceLighting(CaSHLWorld, CaSHLOptions.BlockSize, CaSHLOptions.StopUT, CaSHLOptions.AskForMore);
1296
1297        if (!CaSHLOptions.SkipBL) ToneReproduction(CaSHLWorld.GetBspTree());    // Ward97.cpp
1298        PostProcessBorders(CaSHLWorld);
1299
1300        printf("\n%-50s %s\n", "*** Write Patch coeffs back into SHLMaps ***", GetTimeSinceProgramStart());
1301        CaSHLWorld.PatchesToSHLMaps(Patches);
1302
1303        printf("\n%-50s %s\n", "*** Saving World ***", GetTimeSinceProgramStart());
1304        printf("%s\n", ArgV[1]);
1305        CaSHLWorld.SaveToDisk(ArgV[1]);
1306
1307        WriteLogFileEntry(ArgV[1], CaSHLOptions.StopUT, CaSHLOptions.BlockSize, IterationCount);
1308        printf("\n%-50s %s\n", "COMPLETED.", GetTimeSinceProgramStart());
1309    }
1310    catch (const WorldT::LoadErrorT& E)
1311    {
1312        printf("\nType \"CaSHL\" (without any parameters) for help.\n");
1313        Error(E.Msg);
1314    }
1315    catch (const WorldT::SaveErrorT& E)
1316    {
1317        printf("\nType \"CaSHL\" (without any parameters) for help.\n");
1318        Error(E.Msg);
1319    }
1320
1321    return 0;
1322}
Note: See TracBrowser for help on using the browser.