Fix issue in Rocket.lua script.
[Cafu-Engine.git] / CaLight / CaLightWorld.cpp
blob8028f6424556103e4f95e299a4893ce063c8fb56
1 /*
2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
5 */
7 #include "CaLightWorld.hpp"
8 #include "Bitmap/Bitmap.hpp"
9 #include "ClipSys/CollisionModel_static.hpp"
10 #include "ClipSys/TraceResult.hpp"
11 #include "ClipSys/TraceSolid.hpp"
12 #include "MaterialSystem/Material.hpp"
13 #include "SceneGraph/BspTreeNode.hpp"
16 CaLightWorldT::CaLightWorldT(const char* FileName, ModelManagerT& ModelMan, cf::GuiSys::GuiResourcesT& GuiRes)
17 : m_World(FileName, ModelMan, GuiRes),
18 m_BspTree(m_World.m_StaticEntityData[0]->m_BspTree),
19 m_CollModel(m_World.m_StaticEntityData[0]->m_CollModel)
24 double CaLightWorldT::TraceRay(const Vector3dT& Start, const Vector3dT& Ray) const
26 #if 1
27 const static cf::ClipSys::TracePointT Point;
28 cf::ClipSys::TraceResultT Result(1.0);
30 m_CollModel->TraceConvexSolid(Point, Start, Ray, MaterialT::Clip_Radiance, Result);
32 return Result.Fraction;
33 #else
34 return m_BspTree->TraceRay(Start, Ray, 0.0, 1.0, cf::SceneGraph::ConsiderAll, MaterialT::Clip_Radiance);
35 #endif
39 void CaLightWorldT::SaveToDisk(const char* FileName) const
41 m_World.SaveToDisk(FileName);
45 void CaLightWorldT::CreateLightMapsForEnts(const ArrayT< IntrusivePtrT<cf::GameSys::EntityT> >& AllEnts)
47 // Pre-cache some sample directional vectors.
48 // Code copied from "CaSHL/Init2.cpp".
49 const unsigned long SqrtNrOfSamples=8;
50 ArrayT<Vector3dT> SampleDirs;
52 for (unsigned long SampleX=0; SampleX<SqrtNrOfSamples; SampleX++)
53 for (unsigned long SampleY=0; SampleY<SqrtNrOfSamples; SampleY++)
55 const double Pi =3.14159265358979323846;
56 const double x =double(SampleX)/double(SqrtNrOfSamples) + 0.5/double(SqrtNrOfSamples);
57 const double y =double(SampleY)/double(SqrtNrOfSamples) + 0.5/double(SqrtNrOfSamples);
58 const double theta=2.0*acos(sqrt(1.0-x));
59 const double phi =2.0*Pi*y;
61 SampleDirs.PushBack(Vector3dT(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta)));
65 for (unsigned long EntNr = 1 /*non-world only*/; EntNr < m_World.m_StaticEntityData.Size(); EntNr++)
67 cf::SceneGraph::BspTreeNodeT* EntBspTree = m_World.m_StaticEntityData[EntNr]->m_BspTree;
70 // Obtain the patch meshes for this entity.
71 ArrayT<cf::PatchMeshT> PatchMeshes;
73 for (unsigned long FaceNr=0; FaceNr<EntBspTree->FaceChildren.Size(); FaceNr++)
75 ArrayT< ArrayT< ArrayT<Vector3dT> > > SampleCoords;
77 EntBspTree->FaceChildren[FaceNr]->CreatePatchMeshes(PatchMeshes, SampleCoords, EntBspTree->GetLightMapPatchSize());
80 for (unsigned long OtherNr=0; OtherNr<EntBspTree->OtherChildren.Size(); OtherNr++)
82 ArrayT< ArrayT< ArrayT<Vector3dT> > > SampleCoords;
84 EntBspTree->OtherChildren[OtherNr]->CreatePatchMeshes(PatchMeshes, SampleCoords, EntBspTree->GetLightMapPatchSize());
88 // Fill patch meshes with color.
89 for (unsigned long PatchMeshNr=0; PatchMeshNr<PatchMeshes.Size(); PatchMeshNr++)
91 cf::PatchMeshT& PM=PatchMeshes[PatchMeshNr];
93 for (unsigned long t=0; t<PM.Height; t++)
94 for (unsigned long s=0; s<PM.Width; s++)
96 cf::PatchT& Patch=PM.GetPatch(s, t);
98 Patch.TotalEnergy =Vector3dT(128, 128, 128); // Use mid-grey (128, 128, 128) for normal use, and e.g. (255, 0, 0) for debugging.
99 // Patch.EnergyFromDir=Patch.Normal;
101 if (!Patch.InsideFace) continue;
104 // Gather the averages.
105 double AvgWeightsSum=0.0;
106 Vector3dT AvgColorsSum; // Sum of weighted colors.
107 Vector3dT AvgDirsSum; // Sum of weighted directions.
109 for (unsigned long SampleNr=0; SampleNr<SampleDirs.Size(); SampleNr++)
111 // printf("Mesh %lu, st %lu %lu: coord %s, normal %s, SampleDir %s \n", PatchMeshNr, s, t, convertToString(Patch.Coord).c_str(), convertToString(Patch.Normal).c_str(), convertToString(SampleDirs[SampleNr]).c_str());
113 if (dot(Patch.Normal, SampleDirs[SampleNr])<0) continue; // Don't try sample dirs against the normal.
115 const Vector3dT RayOrigin = AllEnts[EntNr]->GetTransform()->GetOriginWS().AsVectorOfDouble() + Patch.Coord;
116 const double RayLength = 50000.0; // 50 meters max.
117 const double HitFrac = TraceRay(RayOrigin, SampleDirs[SampleNr] * RayLength);
119 if (HitFrac==0.0) continue; // Probably immediately stuck in solid.
120 if (HitFrac==1.0) continue; // Ray did not hit anything.
122 const Vector3dT HitPos = RayOrigin + SampleDirs[SampleNr] * (HitFrac * RayLength - 0.1);
123 const unsigned long LeafNr = m_BspTree->WhatLeaf(HitPos);
125 // printf(" HitFrac %f, HitPos %s, LeafNr %lu\n", HitFrac, convertToString(HitPos).c_str(), LeafNr);
127 for (unsigned long FNr=0; FNr<m_BspTree->Leaves[LeafNr].FaceChildrenSet.Size(); FNr++)
129 cf::SceneGraph::FaceNodeT* FaceNode=m_BspTree->FaceChildren[m_BspTree->Leaves[LeafNr].FaceChildrenSet[FNr]];
130 Vector3fT HitColor;
132 if (!FaceNode->GetLightmapColorNearPosition(HitPos, HitColor, EntBspTree->GetLightMapPatchSize())) continue;
134 const double d1 =dot(Patch.Normal, SampleDirs[SampleNr]);
135 const double d2 =-dot(FaceNode->Polygon.Plane.Normal, SampleDirs[SampleNr]);
136 const double Weight=d1*d2/(HitFrac*HitFrac);
138 // printf(" HitColor %s, d1 %f, d2 %f, Weight %f\n", convertToString(HitColor).c_str(), d1, d2, Weight);
140 AvgWeightsSum+=Weight;
141 AvgColorsSum +=HitColor.AsVectorOfDouble()*255.0*Weight;
142 AvgDirsSum +=SampleDirs[SampleNr]*Weight;
143 break;
147 if (AvgWeightsSum>0)
149 Patch.TotalEnergy =scale(AvgColorsSum, 1.0/AvgWeightsSum);
150 Patch.EnergyFromDir=scale(AvgDirsSum, 1.0/AvgWeightsSum);
156 // Minimal postprocessing.
157 // This is analogous to the first step in PostProcessBorders().
158 for (unsigned long PatchMeshNr=0; PatchMeshNr<PatchMeshes.Size(); PatchMeshNr++)
160 cf::PatchMeshT& PM=PatchMeshes[PatchMeshNr];
162 for (unsigned long t=0; t<PM.Height; t++)
163 for (unsigned long s=0; s<PM.Width; s++)
165 cf::PatchT& Patch=PM.GetPatch(s, t);
167 if (Patch.InsideFace) continue;
169 Patch.TotalEnergy =VectorT(0, 0, 0);
170 Vector3dT AvgDir =VectorT(0, 0, 0); // Do NOT(!) build the average directly in Patch.EnergyFromDir, or else you may end with zero-vector dirs for patches in the midst of faces that have received no light at all!
171 double CoveredArea=0.0;
173 // Der Patch liegt in der Mitte eines 3x3-Feldes bei Koordinate (1,1).
174 for (char y=0; y<=2; y++)
175 for (char x=0; x<=2; x++)
177 if (x==1 && y==1) continue; // Nur die acht umliegenden Patches betrachten.
179 int Nx=int(s+x)-1;
180 int Ny=int(t+y)-1;
182 if (PM.WrapsHorz)
184 // Patches that wrap do *not* duplicate the leftmost column at the right.
185 if (Nx< 0) Nx+=PM.Width;
186 if (Nx>=int(PM.Width)) Nx-=PM.Width;
189 if (PM.WrapsVert)
191 // Patches that wrap do *not* duplicate the topmost column at the bottom.
192 if (Ny< 0) Ny+=PM.Height;
193 if (Ny>=int(PM.Height)) Ny-=PM.Height;
196 if (Nx< 0) continue; // Linken Rand beachten.
197 if (Nx>= int(PM.Width)) continue; // Rechten Rand beachten.
198 if (Ny< 0) continue; // Oberen Rand beachten.
199 if (Ny>=int(PM.Height)) continue; // Unteren Rand beachten.
201 const double RelevantArea=(x!=1 && y!=1) ? 0.25 : 0.5;
202 const cf::PatchT& Neighb =PM.GetPatch(Nx, Ny);
204 if (!Neighb.InsideFace) continue;
206 Patch.TotalEnergy+=Neighb.TotalEnergy *RelevantArea;
207 AvgDir +=Neighb.EnergyFromDir*RelevantArea;
208 CoveredArea +=RelevantArea;
211 if (CoveredArea>0.0001)
213 Patch.TotalEnergy /=CoveredArea;
214 Patch.EnergyFromDir+=AvgDir/CoveredArea;
220 // Write back.
221 for (unsigned long PatchMeshNr=0; PatchMeshNr<PatchMeshes.Size(); PatchMeshNr++)
223 const cf::PatchMeshT& PM =PatchMeshes[PatchMeshNr];
224 cf::SceneGraph::GenericNodeT* PM_Node=const_cast<cf::SceneGraph::GenericNodeT*>(PM.Node);
226 // Need a non-const pointer to the "source" NodeT of the patch mesh here.
227 PM_Node->BackToLightMap(PM, EntBspTree->GetLightMapPatchSize());