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