1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 2014-2016, The OpenClonk Team and contributors
5 *
6 * Distributed under the terms of the ISC license; see accompanying file
7 * "COPYING" for details.
8 *
9 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10 * See accompanying file "TRADEMARK" for details.
11 *
12 * To redistribute this file separately, substitute the full license texts
13 * for the above references.
14 */
15
16 #include "C4Include.h"
17 #include "C4ForbidLibraryCompilation.h"
18 #include "landscape/fow/C4FoWAmbient.h"
19 #include "landscape/fow/C4FoW.h"
20 #include "graphics/C4Draw.h"
21
22 namespace
23 {
24
25 template<typename LightMap>
AmbientForPix(int x0,int y0,double R,const LightMap & light_map)26 double AmbientForPix(int x0, int y0, double R, const LightMap& light_map)
27 {
28 double d = 0.;
29
30 const int Ri = static_cast<int>(R);
31 for(int y = 1; y <= Ri; ++y)
32 {
33 // quarter circle
34 int max_x = static_cast<int>(sqrt(R * R - y * y));
35 for(int x = 1; x <= max_x; ++x)
36 {
37 const double l = sqrt(x*x + y*y);
38 assert(l <= R);
39
40 if(light_map(x0 + x, y0 + y)) d += 1. / l;
41 if(light_map(x0 + x, y0 - y)) d += 1. / l;
42 if(light_map(x0 - x, y0 - y)) d += 1. / l;
43 if(light_map(x0 - x, y0 + y)) d += 1. / l;
44 }
45
46 // Vertical/Horizontal lines
47 const double l = static_cast<double>(y);
48 if(light_map(x0 + y, y0)) d += 1. / l;
49 if(light_map(x0 - y, y0)) d += 1. / l;
50 if(light_map(x0, y0 + y)) d += 1. / l;
51 if(light_map(x0, y0 - y)) d += 1. / l;
52 }
53
54 // Central pix
55 if(light_map(x0, y0)) d += 2 * sqrt(M_PI); // int_0^2pi int_0^1/sqrt(pi) 1/r dr r dphi
56
57 return d;
58 }
59
60 // Everything is illuminated, independent of the landscape
61 // This is used to obtain the normalization factor
62 struct LightMapFull {
63 LightMapFull() = default;
operator ()__anon162be0fd0111::LightMapFull64 bool operator()(int x, int y) const { return true; }
65 };
66
67 struct LightMapZoom {
LightMapZoom__anon162be0fd0111::LightMapZoom68 LightMapZoom(const C4Landscape& landscape, double sx, double sy):
69 Landscape(landscape), sx(sx), sy(sy) {}
70
71 // Returns whether zoomed coordinate is LightMap or not
operator ()__anon162be0fd0111::LightMapZoom72 bool operator()(int x, int y) const
73 {
74 // Landscape coordinates
75 const int lx = Clamp(static_cast<int>((x + 0.5) * sx), 0, Landscape.GetWidth() - 1);
76 const int ly = Clamp(static_cast<int>((y + 0.5) * sy), 0, Landscape.GetHeight() - 1);
77 // LightMap check
78 return ::Landscape._GetLight(lx, ly);
79 }
80
81 const C4Landscape& Landscape;
82 const double sx;
83 const double sy;
84 };
85
86 } // anonymous namespace
87
88 C4FoWAmbient::C4FoWAmbient() = default;
89
~C4FoWAmbient()90 C4FoWAmbient::~C4FoWAmbient()
91 {
92 Clear();
93 }
94
Clear()95 void C4FoWAmbient::Clear()
96 {
97 #ifndef USE_CONSOLE
98 if(Tex != 0) glDeleteTextures(1, &Tex);
99 Tex = 0;
100 #endif
101 Resolution = Radius = FullCoverage = 0.;
102 SizeX = SizeY = 0;
103 LandscapeX = LandscapeY = 0;
104 Brightness = 1.;
105 }
106
CreateFromLandscape(const C4Landscape & landscape,double resolution,double radius,double full_coverage)107 void C4FoWAmbient::CreateFromLandscape(const C4Landscape& landscape, double resolution, double radius, double full_coverage)
108 {
109 assert(resolution >= 1.);
110 assert(radius >= 1.);
111 assert(full_coverage > 0 && full_coverage <= 1.);
112
113 // Clear old map
114 Clear();
115
116 Resolution = resolution;
117 Radius = radius;
118 FullCoverage = full_coverage;
119
120 // Number of zoomed pixels
121 LandscapeX = Landscape.GetWidth();
122 LandscapeY = Landscape.GetHeight();
123 SizeX = std::min<unsigned int>(static_cast<unsigned int>(ceil(LandscapeX / resolution)), pDraw->MaxTexSize);
124 SizeY = std::min<unsigned int>(static_cast<unsigned int>(ceil(LandscapeY / resolution)), pDraw->MaxTexSize);
125
126 #ifndef USE_CONSOLE
127 glGenTextures(1, &Tex);
128 glBindTexture(GL_TEXTURE_2D, Tex);
129 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
130 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
131 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
133 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, SizeX, SizeY, 0, GL_RED, GL_FLOAT, nullptr);
134
135 const C4TimeMilliseconds begin = C4TimeMilliseconds::Now();
136 UpdateFromLandscape(landscape, C4Rect(0, 0, Landscape.GetWidth(), Landscape.GetHeight()));
137 uint32_t dt = C4TimeMilliseconds::Now() - begin;
138 LogF("Created %ux%u ambient map in %g secs", SizeX, SizeY, dt / 1000.);
139 #endif
140 }
141
UpdateFromLandscape(const C4Landscape & landscape,const C4Rect & update)142 void C4FoWAmbient::UpdateFromLandscape(const C4Landscape& landscape, const C4Rect& update)
143 {
144 #ifndef USE_CONSOLE
145 // Nothing to do?
146 if(update.Wdt == 0 || update.Hgt == 0) return;
147
148 assert(Tex != 0);
149
150 // Factor to go from zoomed to landscape coordinates
151 const double zoom_x = static_cast<double>(Landscape.GetWidth()) / SizeX;
152 const double zoom_y = static_cast<double>(Landscape.GetHeight()) / SizeY;
153 // Update region in zoomed coordinates
154 const unsigned int left = std::max(static_cast<int>( (update.x - Radius) / zoom_x), 0);
155 const unsigned int right = std::min(static_cast<unsigned int>( (update.x + update.Wdt + Radius) / zoom_x), SizeX - 1) + 1;
156 const unsigned int top = std::max(static_cast<int>( (update.y - Radius) / zoom_y), 0);
157 const unsigned int bottom = std::min(static_cast<unsigned int>( (update.y + update.Hgt + Radius) / zoom_y), SizeY - 1) + 1;
158 assert(right > left);
159 assert(bottom > top);
160 // Zoomed radius
161 const double R = Radius / sqrt(zoom_x * zoom_y);
162 // Normalization factor with the full circle
163 // The analytic result is 2*R*M_PI, and this number is typically close to it
164 const double norm = AmbientForPix(0, 0, R, LightMapFull()) * FullCoverage;
165 // Create the ambient map
166 LightMapZoom light_mapZoom(landscape, zoom_x, zoom_y);
167 float* ambient = new float[(right - left) * (bottom - top)];
168 for(unsigned int y = top; y < bottom; ++y)
169 {
170 for(unsigned int x = left; x < right; ++x)
171 {
172 ambient[(y - top) * (right - left) + (x - left)] = std::min(AmbientForPix(x, y, R, light_mapZoom) / norm, 1.0);
173 }
174 }
175
176 #if 0
177 CSurface8 debug(SizeX, SizeY);
178 for(unsigned int y = 0; y < SizeY; ++y)
179 {
180 for(unsigned int x = 0; x < SizeX; ++x)
181 {
182 debug.SetPix(x, y, int(ambient[y * SizeX + x] * 255. + 0.5));
183 }
184 }
185
186 CStdPalette pal;
187 for(int i = 0; i < 256; ++i)
188 pal.Colors[i] = i + (i << 8) + (i << 16);
189 debug.Save("Ambient.bmp", &pal);
190 #endif
191
192 // Update the texture
193 glBindTexture(GL_TEXTURE_2D, Tex);
194 glTexSubImage2D(GL_TEXTURE_2D, 0, left, top, (right - left), (bottom - top), GL_RED, GL_FLOAT, ambient);
195 delete[] ambient;
196 #endif
197 }
198
GetFragTransform(const FLOAT_RECT & vpRect,const C4Rect & clipRect,const C4Rect & outRect,float ambientTransform[6]) const199 void C4FoWAmbient::GetFragTransform(const FLOAT_RECT& vpRect, const C4Rect& clipRect, const C4Rect& outRect, float ambientTransform[6]) const
200 {
201 C4FragTransform trans;
202 // Invert Y coordinate
203 trans.Scale(1, -1);
204 trans.Translate(0, outRect.Hgt);
205 // Clip offset
206 trans.Translate(-clipRect.x, -clipRect.y);
207 // Clip normalization (0,0 -> 1,1)
208 trans.Scale(1.0f / clipRect.Wdt, 1.0f / clipRect.Hgt);
209 // Viewport normalization
210 trans.Scale(vpRect.right - vpRect.left, vpRect.bottom - vpRect.top);
211 // Viewport offset
212 trans.Translate(vpRect.left, vpRect.top);
213 // Landscape normalization
214 trans.Scale(1.0f / LandscapeX, 1.0f / LandscapeY);
215
216 // Extract matrix
217 trans.Get2x3(ambientTransform);
218 }
219