OpenClonk
C4FoWLight.cpp
Go to the documentation of this file.
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"
18 
19 #ifndef USE_CONSOLE
20 
25 #include "lib/StdColors.h"
26 #include "player/C4PlayerList.h"
27 #include "player/C4Player.h"
28 
30  : iX(fixtoi(pObj->fix_x)),
31  iY(fixtoi(pObj->fix_y)),
32  iReach(pObj->lightRange),
33  iFadeout(pObj->lightFadeoutRange),
34  iSize(20), gBright(0.5), colorR(1.0), colorG(1.0), colorB(1.0),
35  colorV(1.0), colorL(1.0),
36  pNext(nullptr),
37  pObj(pObj),
38  sections(4)
39 {
40  sections[0] = new C4FoWLightSection(this,0);
41  sections[1] = new C4FoWLightSection(this,90);
42  sections[2] = new C4FoWLightSection(this,180);
43  sections[3] = new C4FoWLightSection(this,270);
44 }
45 
47 {
48  for(auto & section : sections)
49  delete section;
50 }
51 
53 {
54  for(auto & section : sections)
55  section->Invalidate(r);
56 }
57 
58 void C4FoWLight::SetReach(int32_t iReach2, int32_t iFadeout2)
59 {
60  // Fadeout changes don't matter
61  iFadeout = iFadeout2;
62 
63  if (iReach == iReach2) return;
64 
65  if (iReach2 < iReach)
66  {
67  // Reach decreased? Prune beams
68  iReach = iReach2;
69  for(auto & section : sections)
70  section->Prune(iReach);
71 
72  } else {
73 
74  // Reach increased? Dirty beams that might get longer now
75  iReach = iReach2;
76  for(auto & section : sections)
77  section->Dirty(iReach);
78  }
79 }
80 
81 void C4FoWLight::SetColor(uint32_t iValue)
82 {
83  colorR = GetRedValue(iValue) / 255.0f;
84  colorG = GetGreenValue(iValue) / 255.0f;
85  colorB = GetBlueValue(iValue) / 255.0f;
86 
87  float min = std::min(colorR, std::min(colorG, colorB));
88  colorV = std::max(std::max(colorR, std::max(colorG, colorB)), 1e-3f); // prevent division by 0
89  colorL = (min + colorV) / 2.0f;
90 
91  // maximize color, so that dark colors will not be desaturated after normalization
92  colorR = std::min(colorR / colorV, 1.0f);
93  colorG = std::min(colorG / colorV, 1.0f);
94  colorB = std::min(colorB / colorV, 1.0f);
95 }
96 
98 {
99  // Update position from object.
100  int32_t iNX = fixtoi(pObj->fix_x), iNY = fixtoi(pObj->fix_y);
101  // position may be affected by LightOffset property
102  C4ValueArray *light_offset = pObj->GetPropertyArray(P_LightOffset);
103  if (light_offset)
104  {
105  iNX += light_offset->GetItem(0).getInt();
106  iNY += light_offset->GetItem(1).getInt();
107  }
108  // Clear if we moved in any way
109  if (iNX != iX || iNY != iY)
110  {
111  for(auto & section : sections)
112  section->Prune(0);
113  iX = iNX; iY = iNY;
114  }
115 
116  for(auto & section : sections)
117  section->Update(Rec);
118 }
119 
120 void C4FoWLight::Render(C4FoWRegion *region, const C4TargetFacet *onScreen, C4ShaderCall& call)
121 {
122  TriangleList triangles;
123 
124  bool clip = false;
125 
126  for(auto & section : sections)
127  {
128  TriangleList sectionTriangles = section->CalculateTriangles(region);
129 
130  // if the triangles of one section are clipped completely, the neighbouring triangles
131  // must be marked as clipped
132  if(!triangles.empty()) triangles.rbegin()->clipRight |= clip;
133  if(!sectionTriangles.empty()) sectionTriangles.begin()->clipLeft |= clip;
134 
135  clip = sectionTriangles.empty();
136  triangles.splice(triangles.end(), sectionTriangles);
137  }
138 
139  CalculateFanMaxed(triangles);
140  CalculateIntermediateFadeTriangles(triangles);
141 
142  std::unique_ptr<C4FoWDrawStrategy>& strategy = onScreen ? OnScreenStrategy : OffScreenStrategy;
143  if (!strategy.get())
144  {
145  if (onScreen)
146  strategy = std::make_unique<C4FoWDrawWireframeStrategy>(this, onScreen);
147  else
148  strategy = std::make_unique<C4FoWDrawLightTextureStrategy>(this);
149  }
150 
151  strategy->Begin(region);
152 
153  DrawFan(strategy.get(), triangles);
154  DrawFanMaxed(strategy.get(), triangles);
155  DrawFade(strategy.get(), triangles);
156  DrawIntermediateFadeTriangles(strategy.get(), triangles);
157 
158  strategy->End(call);
159 }
160 
161 void C4FoWLight::CalculateFanMaxed(TriangleList &triangles) const
162 {
163  for (auto & tri : triangles)
164  {
165  // Is the left point close enough that normals don't max out?
166  float dist = sqrt(GetSquaredDistanceTo(tri.fanLX, tri.fanLY));
167  if (dist <= getNormalSize()) {
168  tri.nfanLX = tri.fanLX;
169  tri.nfanLY = tri.fanLY;
170  } else {
171  // Otherwise, calculate point where they do. We will add a seperate
172  // triangle/quad later on to capture that.
173  float f = float(getNormalSize() / dist);
174  tri.nfanLX = f * tri.fanLX + (1.0f - f) * getX();
175  tri.nfanLY = f * tri.fanLY + (1.0f - f) * getY();
176  }
177 
178  // Same for the right point
179  dist = sqrt(GetSquaredDistanceTo(tri.fanRX, tri.fanRY));
180  if (dist <= getNormalSize()) {
181  tri.nfanRX = tri.fanRX;
182  tri.nfanRY = tri.fanRY;
183  } else {
184  float f = float(getNormalSize()) / dist;
185  tri.nfanRX = f * tri.fanRX + (1.0f - f) * getX();
186  tri.nfanRY = f * tri.fanRY + (1.0f - f) * getY();
187  }
188  }
189 }
190 
191 void C4FoWLight::ProjectPointOutward(float &x, float &y, float maxDistance) const
192 {
193  float distanceDifference = std::min(maxDistance, (float) getTotalReach()) / sqrt((x - getX()) * (x - getX()) + (y - getY()) * (y - getY()));
194 
195  x = getX() + distanceDifference * (x-getX());
196  y = getY() + distanceDifference * (y-getY());
197 }
198 
199 void C4FoWLight::CalculateIntermediateFadeTriangles(TriangleList &triangles) const
200 {
201  for (TriangleList::iterator it = triangles.begin(), nextIt = it; it != triangles.end(); ++it)
202  {
203  // wrap around
204  ++nextIt;
205  if(nextIt == triangles.end()) nextIt = triangles.begin();
206 
207  C4FoWBeamTriangle &tri = *it, &nextTri = *nextIt; // just for convenience
208 
209  // don't calculate if it should not be drawn anyway
210  if (tri.clipRight || nextTri.clipLeft) continue;
211 
212  float distFadeR = GetSquaredDistanceTo(tri.fadeRX, tri.fadeRY);
213  float distNextFadeL = GetSquaredDistanceTo(nextTri.fadeLX, nextTri.fadeLY);
214  float distFanR = GetSquaredDistanceTo(tri.fanRX, tri.fanRY);
215  float distNextFanL = GetSquaredDistanceTo(nextTri.fanLX, nextTri.fanLY);
216 
217  // an extra intermediate fade point is only necessary on cliffs
218  tri.descending = distFanR > distNextFanL;
219  if (tri.descending) {
220  if (distFanR < distNextFadeL)
221  {
222  tri.fadeIX = nextTri.fadeLX;
223  tri.fadeIY = nextTri.fadeLY;
224  }
225  else
226  {
227  tri.fadeIX = (tri.fanRX + nextTri.fadeLX) / 2;
228  tri.fadeIY = (tri.fanRY + nextTri.fadeLY) / 2;
229  ProjectPointOutward(tri.fadeIX, tri.fadeIY, sqrt(distFadeR));
230  }
231  }
232  else
233  {
234  if (distNextFanL < distFadeR)
235  {
236  tri.fadeIX = tri.fadeRX;
237  tri.fadeIY = tri.fadeRY;
238  }
239  else
240  {
241  tri.fadeIX = (tri.fadeRX + nextTri.fanLX) / 2;
242  tri.fadeIY = (tri.fadeRY + nextTri.fanLY) / 2;
243  ProjectPointOutward(tri.fadeIX, tri.fadeIY, sqrt(distNextFadeL));
244  }
245  }
246  }
247 }
248 
249 
250 void C4FoWLight::DrawFan(C4FoWDrawStrategy* pen, TriangleList &triangles) const
251 {
252  pen->BeginFan();
253  if (!triangles.empty()) pen->DrawLightVertex(getX(), getY());
254 
255  for (TriangleList::iterator it = triangles.begin(), nextIt = it; it != triangles.end(); ++it)
256  {
257  // wrap around
258  ++nextIt;
259  if(nextIt == triangles.end()) nextIt = triangles.begin();
260 
261  C4FoWBeamTriangle &tri = *it, &nextTri = *nextIt; // just for convenience
262 
263  pen->DrawLightVertex(tri.nfanLX, tri.nfanLY);
264 
265  if(nextIt == triangles.begin() || nextTri.nfanLX != tri.nfanRX || nextTri.nfanLY != tri.nfanRY)
266  pen->DrawLightVertex(tri.nfanRX, tri.nfanRY);
267  }
268  pen->EndFan();
269 }
270 
271 void C4FoWLight::DrawFanMaxed(C4FoWDrawStrategy* pen, TriangleList &triangles) const
272 {
273  pen->BeginFanMaxed();
274  for (TriangleList::iterator it = triangles.begin(), nextIt = it; it != triangles.end(); ++it)
275  {
276  // Wrap around for next triangle
277  ++nextIt; if(nextIt == triangles.end()) nextIt = triangles.begin();
278  C4FoWBeamTriangle &tri = *it, &nextTri = *nextIt;
279 
280  // First for the current beam
281  if (tri.nfanLX != tri.nfanRX || tri.nfanLY != tri.nfanRY)
282  {
283  pen->DrawLightVertex(tri.nfanLX, tri.nfanLY);
284  pen->DrawLightVertex(tri.nfanRX, tri.nfanRY);
285  pen->DrawLightVertex(tri.fanRX, tri.fanRY);
286  pen->DrawLightVertex(tri.fanLX, tri.fanLY);
287  }
288  // Then for the space in-between
289  if (tri.nfanRX != nextTri.nfanLX || tri.nfanRY != nextTri.nfanLY)
290  {
291  pen->DrawLightVertex(tri.nfanRX, tri.nfanRY);
292  pen->DrawLightVertex(nextTri.nfanLX, nextTri.nfanLY);
293  pen->DrawLightVertex(nextTri.fanLX, nextTri.fanLY);
294  pen->DrawLightVertex(tri.fanRX, tri.fanRY);
295  }
296  }
297  pen->EndFanMaxed();
298 }
299 
300 void C4FoWLight::DrawFade(C4FoWDrawStrategy* pen, TriangleList &triangles) const
301 {
302  pen->BeginFade();
303 
304  for (auto & tri : triangles)
305  {
306  // The quad will be empty if fan points match
307  if (tri.fanLX == tri.fanRX && tri.fanLY == tri.fanRY) continue;
308 
309  pen->DrawLightVertex(tri.fanLX, tri.fanLY);
310  pen->DrawLightVertex(tri.fanRX, tri.fanRY);
311  pen->DrawDarkVertex(tri.fadeRX, tri.fadeRY);
312  pen->DrawDarkVertex(tri.fadeLX, tri.fadeLY);
313  }
314  pen->EndFade();
315 }
316 
317 void C4FoWLight::DrawIntermediateFadeTriangles(C4FoWDrawStrategy* pen, TriangleList &triangles) const
318 {
319 
320  for (TriangleList::iterator it = triangles.begin(), nextIt = it; it != triangles.end(); ++it)
321  {
322  // wrap around
323  ++nextIt;
324  if(nextIt == triangles.end()) nextIt = triangles.begin();
325 
326  C4FoWBeamTriangle &tri = *it, &nextTri = *nextIt; // just for convenience
327 
328  // no inter-fade triangles when it should be clipped
329  if (tri.clipRight || nextTri.clipLeft) continue;
330 
331  pen->BeginIntermediateFade();
332 
333  if (tri.descending) {
334 
335  pen->DrawLightVertex(tri.fanRX, tri.fanRY);
336  pen->DrawLightVertex(nextTri.fanLX, nextTri.fanLY);
337  pen->DrawDarkVertex(nextTri.fadeLX, nextTri.fadeLY);
338 
339  // if necessary
340  if (tri.fadeIY != nextTri.fadeLY || tri.fadeIX != nextTri.fadeLX) {
341  pen->DrawDarkVertex(tri.fadeIX, tri.fadeIY);
342  }
343 
344  pen->DrawDarkVertex(tri.fadeRX, tri.fadeRY);
345 
346  } else {
347 
348  pen->DrawLightVertex(nextTri.fanLX, nextTri.fanLY);
349  pen->DrawDarkVertex(nextTri.fadeLX, nextTri.fadeLY);
350 
351  // if necessary
352  if (tri.fadeIY != tri.fadeRY || tri.fadeIX != tri.fadeRX) {
353  pen->DrawDarkVertex(tri.fadeIX, tri.fadeIY);
354  }
355 
356  pen->DrawDarkVertex(tri.fadeRX, tri.fadeRY);
357  pen->DrawLightVertex(tri.fanRX, tri.fanRY);
358  }
359 
360  pen->EndIntermediateFade();
361  }
362 }
363 
365 {
366  // check if attached to an object that is not hostile to the given player
367  if (!pObj || !player) return true;
368  return !::Hostile(pObj->Owner,player->Number);
369 }
370 
371 #endif
int32_t Hostile(int32_t plr1, int32_t plr2)
int fixtoi(const C4Fixed &x)
Definition: C4Real.h:259
@ P_LightOffset
#define GetRedValue(rgb)
Definition: StdColors.h:29
#define GetGreenValue(rgb)
Definition: StdColors.h:28
#define GetBlueValue(rgb)
Definition: StdColors.h:27
int iSize
Definition: TstC4NetIO.cpp:32
virtual void BeginFan()
virtual void BeginIntermediateFade()
virtual void DrawLightVertex(float x, float y)
virtual void EndFade()
virtual void EndIntermediateFade()
virtual void DrawDarkVertex(float x, float y)
virtual void EndFanMaxed()
virtual void BeginFanMaxed()
virtual void BeginFade()
virtual void EndFan()
C4FoWLight(C4Object *pObj)
Definition: C4FoWLight.cpp:29
void SetReach(int32_t iReach, int32_t iFadeout)
Definition: C4FoWLight.cpp:58
int32_t getX() const
Definition: C4FoWLight.h:58
void SetColor(uint32_t iValue)
Definition: C4FoWLight.cpp:81
void Invalidate(C4Rect r)
Definition: C4FoWLight.cpp:52
void Update(C4Rect r)
Definition: C4FoWLight.cpp:97
int32_t getTotalReach() const
Definition: C4FoWLight.h:62
bool IsVisibleForPlayer(C4Player *player) const
Definition: C4FoWLight.cpp:364
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen, C4ShaderCall &call)
Definition: C4FoWLight.cpp:120
int32_t getNormalSize() const
Definition: C4FoWLight.h:64
int32_t getY() const
Definition: C4FoWLight.h:59
C4Real fix_y
Definition: C4Object.h:123
C4Real fix_x
Definition: C4Object.h:123
int32_t Owner
Definition: C4Object.h:108
C4ValueArray * GetPropertyArray(C4PropertyName n) const
Definition: C4PropList.cpp:758
Definition: C4Rect.h:28
const C4Value & GetItem(int32_t iElem) const
Definition: C4ValueArray.h:38
int32_t getInt() const
Definition: C4Value.h:112