| 1 | /* |
|---|
| 2 | ================================================================================= |
|---|
| 3 | This file is part of Cafu, the open-source game engine and graphics engine |
|---|
| 4 | for multiplayer, cross-platform, real-time 3D action. |
|---|
| 5 | Copyright (C) 2002-2012 Carsten Fuchs Software. |
|---|
| 6 | |
|---|
| 7 | Cafu is free software: you can redistribute it and/or modify it under the terms |
|---|
| 8 | of the GNU General Public License as published by the Free Software Foundation, |
|---|
| 9 | either version 3 of the License, or (at your option) any later version. |
|---|
| 10 | |
|---|
| 11 | Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|---|
| 12 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
|---|
| 13 | PURPOSE. See the GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with Cafu. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | |
|---|
| 18 | For support and more information about Cafu, visit us at <http://www.cafu.de>. |
|---|
| 19 | ================================================================================= |
|---|
| 20 | */ |
|---|
| 21 | |
|---|
| 22 | struct SunT |
|---|
| 23 | { |
|---|
| 24 | MaterialT* Material; |
|---|
| 25 | VectorT SunIrradiance; |
|---|
| 26 | VectorT LightRevDir; |
|---|
| 27 | }; |
|---|
| 28 | |
|---|
| 29 | |
|---|
| 30 | void 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 | |
|---|
| 83 | void 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 | } |
|---|