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 "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
virtual void EndFade()
void SetColor(uint32_t iValue)
Definition: C4FoWLight.cpp:81
int32_t Number
Definition: C4Player.h:86
#define GetGreenValue(rgb)
Definition: StdColors.h:28
int32_t getY() const
Definition: C4FoWLight.h:59
void Update(C4Rect r)
Definition: C4FoWLight.cpp:97
virtual void BeginFade()
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen, C4ShaderCall &call)
Definition: C4FoWLight.cpp:120
Definition: C4Rect.h:27
#define GetRedValue(rgb)
Definition: StdColors.h:29
void Invalidate(C4Rect r)
Definition: C4FoWLight.cpp:52
int32_t getX() const
Definition: C4FoWLight.h:58
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:62
#define GetBlueValue(rgb)
Definition: StdColors.h:27
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:785
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:64
C4Real fix_y
Definition: C4Object.h:125
virtual void BeginIntermediateFade()
C4FoWLight(C4Object *pObj)
Definition: C4FoWLight.cpp:29
bool IsVisibleForPlayer(C4Player *player) const
Definition: C4FoWLight.cpp:364
int fixtoi(const C4Fixed &x)
Definition: C4Real.h:259
void SetReach(int32_t iReach, int32_t iFadeout)
Definition: C4FoWLight.cpp:58
int iSize
Definition: TstC4NetIO.cpp:32