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.
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
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
;
34 return m_BspTree
->TraceRay(Start
, Ray
, 0.0, 1.0, cf::SceneGraph::ConsiderAll
, MaterialT::Clip_Radiance
);
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
]];
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
;
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.
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
;
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
;
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());