OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
lightgroup.cpp
Go to the documentation of this file.
1#include "lightgroup.h"
2
3#include <Tempest/Dir>
4#include <Tempest/Log>
5
6#include <zenkit/Archive.hh>
7
8#include "graphics/shaders.h"
10#include "utils/string_frm.h"
11#include "world/world.h"
12#include "utils/dbgpainter.h"
13#include "gothic.h"
14
15using namespace Tempest;
16
17static float clampRange(float r) {
18 return std::min(r, 2000.f);
19 //return r;
20 }
21
22LightGroup::Light::Light(LightGroup::Light&& oth):owner(oth.owner), id(oth.id) {
23 oth.owner = nullptr;
24 }
25
27 std::swap(owner,other.owner);
28 std::swap(id,other.id);
29 return *this;
30 }
31
33 if(owner!=nullptr)
34 owner->free(id);
35 }
36
37void LightGroup::Light::setPosition(float x, float y, float z) {
38 setPosition(Vec3(x,y,z));
39 }
40
41void LightGroup::Light::setPosition(const Vec3& p) {
42 if(owner==nullptr)
43 return;
44 auto& data = owner->lightSourceDesc[id];
45 data.setPosition(p);
46
47 auto& ssbo = owner->lightSourceData[id];
48 ssbo.pos = p;
49 owner->markAsDurty(id);
50 }
51
53 if(owner==nullptr)
54 return;
55 auto& data = owner->lightSourceDesc[id];
56 data.setEnabled(e);
57
58 auto& ssbo = owner->lightSourceData[id];
59 ssbo.range = 0;
60 owner->markAsDurty(id);
61 }
62
64 if(owner==nullptr)
65 return;
66 auto& data = owner->lightSourceDesc[id];
67 data.setRange(r);
68
69 auto& ssbo = owner->lightSourceData[id];
70 ssbo.range = data.isEnabled() ? clampRange(r) : 0;
71 owner->markAsDurty(id);
72 }
73
74void LightGroup::Light::setColor(const Vec3& c) {
75 if(owner==nullptr)
76 return;
77 auto& data = owner->lightSourceDesc[id];
78 data.setColor(c);
79
80 auto& ssbo = owner->lightSourceData[id];
81 ssbo.color = c;
82 owner->markAsDurty(id);
83 }
84
85void LightGroup::Light::setColor(const std::vector<Vec3>& c, float fps, bool smooth) {
86 if(owner==nullptr)
87 return;
88 auto& data = owner->lightSourceDesc[id];
89 data.setColor(c,fps,smooth);
90
91 auto& ssbo = owner->lightSourceData[id];
92 ssbo.color = data.currentColor();
93 owner->markAsDurty(id);
94 }
95
97 if(owner==nullptr)
98 return;
99 auto& data = owner->lightSourceDesc[id];
100 data.setTimeOffset(t);
101 }
102
104 if(owner==nullptr)
105 return 0;
106 auto& data = owner->lightSourceDesc[id];
107 return data.effectPrefferedTime();
108 }
109
111 try {
112 std::unique_ptr<zenkit::Read> read;
113 auto zen = Resources::openReader("LIGHTPRESETS.ZEN", read);
114
115 zenkit::ArchiveObject obj {};
116 auto count = zen->read_int();
117 for(int i = 0; i < count; ++i) {
118 zen->read_object_begin(obj);
119
120 zenkit::LightPreset preset {};
121 preset.load(*zen, Gothic::inst().version().game == 1 ? zenkit::GameVersion::GOTHIC_1
122 : zenkit::GameVersion::GOTHIC_2);
123 presets.emplace_back(std::move(preset));
124
125 if(!zen->read_object_end()) {
126 zen->skip_object(true);
127 }
128 }
129 }
130 catch(...) {
131 Log::e("unable to load Zen-file: \"LIGHTPRESETS.ZEN\"");
132 }
133 }
134
135LightGroup::Light LightGroup::add(const zenkit::LightPreset& vob) {
136 LightSource l;
137 l.setPosition(Vec3(0, 0, 0));
138 l.setDebugName(vob.preset);
139
140 if(!vob.range_animation_scale.empty()) {
141 l.setRange(vob.range_animation_scale,vob.range,vob.range_animation_fps,vob.range_animation_smooth);
142 } else {
143 l.setRange(vob.range);
144 }
145
146 if(!vob.color_animation_list.empty()) {
147 l.setColor(vob.color_animation_list,vob.color_animation_fps,vob.color_animation_smooth);
148 } else {
149 l.setColor(Vec3(vob.color.r / 255.f, vob.color.g / 255.f, vob.color.b / 255.f));
150 }
151
152 std::lock_guard<std::mutex> guard(sync);
153 size_t id = alloc(l.isDynamic());
154 auto lx = Light(*this, id);
155
156 auto& ssbo = lightSourceData[lx.id];
157 ssbo.pos = l.position();
158 ssbo.range = l.isEnabled() ? clampRange(l.range()) : 0;
159 ssbo.color = l.color();
160
161 auto& data = lightSourceDesc[lx.id];
162 data = std::move(l);
163
164 markAsDurtyNoSync(lx.id);
165 return lx;
166 }
167
168LightGroup::Light LightGroup::add(const zenkit::VLight& vob) {
169 auto l = add(static_cast<const zenkit::LightPreset&>(vob));
170 l.setPosition(Vec3(vob.position.x,vob.position.y,vob.position.z));
171 return l;
172 }
173
174LightGroup::Light LightGroup::add(std::string_view preset) {
175 return add(findPreset(preset));
176 }
177
179 static bool ddraw=false;
180 if(!ddraw)
181 return;
182
183 //p.setPen(Color(1,0,0,0.01f));
184 p.setPen(Color(1,0,0,1.f));
185
186 for(auto& i:lightSourceDesc) {
187 auto pt = i.position();
188 p.drawText(pt, i.debugName());
189
190 float l = 10;
191 p.drawLine(pt-Vec3(l,0,0),pt+Vec3(l,0,0));
192 p.drawLine(pt-Vec3(0,l,0),pt+Vec3(0,l,0));
193 p.drawLine(pt-Vec3(0,0,l),pt+Vec3(0,0,l));
194 /*
195 float r = i.range();
196 auto pt = i.position();
197 Vec3 px[9] = {};
198 px[0] = pt+Vec3(-r,-r,-r);
199 px[1] = pt+Vec3( r,-r,-r);
200 px[2] = pt+Vec3( r, r,-r);
201 px[3] = pt+Vec3(-r, r,-r);
202 px[4] = pt+Vec3(-r,-r, r);
203 px[5] = pt+Vec3( r,-r, r);
204 px[6] = pt+Vec3( r, r, r);
205 px[7] = pt+Vec3(-r, r, r);
206 px[8] = pt;
207
208 for(auto& i:px) {
209 p.mvp.project(i.x,i.y,i.z);
210 i.x = (i.x+1.f)*0.5f;
211 i.y = (i.y+1.f)*0.5f;
212 }
213
214 int x = int(px[8].x*float(p.w));
215 int y = int(px[8].y*float(p.h));
216
217 int x0 = x, x1 = x;
218 int y0 = y, y1 = y;
219 float z0=px[8].z, z1=px[8].z;
220
221 for(auto& i:px) {
222 int x = int(i.x*float(p.w));
223 int y = int(i.y*float(p.h));
224 x0 = std::min(x0, x);
225 y0 = std::min(y0, y);
226 x1 = std::max(x1, x);
227 y1 = std::max(y1, y);
228 z0 = std::min(z0, i.z);
229 z1 = std::max(z1, i.z);
230 }
231
232 if(z1<0.f || z0>1.f)
233 continue;
234 if(x1<0 || x0>int(p.w))
235 continue;
236 if(y1<0 || y0>int(p.h))
237 continue;
238
239 cnt++;
240 p.painter.drawRect(x0,y0,x1-x0,y1-y0);
241 p.painter.drawRect(x0,y0,3,3);
242 */
243 }
244
245 string_frm name("light count = ", lightSourceDesc.size());
246 p.drawText(10,50,name);
247 }
248
249size_t LightGroup::alloc(bool dynamic) {
250 if(freeList.size()>0) {
251 auto ret = freeList.back();
252 freeList.pop_back();
253 if(dynamic)
254 animatedLights.insert(ret);
255 markAsDurtyNoSync(ret);
256 return ret;
257 }
258 lightSourceData.emplace_back();
259 lightSourceDesc.emplace_back();
260 duryBit.resize((lightSourceData.size()+32u-1u)/32u);
261
262 auto ret = lightSourceData.size()-1;
263 if(dynamic)
264 animatedLights.insert(ret);
265 markAsDurtyNoSync(ret);
266 return ret;
267 }
268
269void LightGroup::free(size_t id) {
270 std::lock_guard<std::mutex> guard(sync);
271 markAsDurtyNoSync(id);
272 animatedLights.erase(id);
273 if(id+1==lightSourceData.size()) {
274 lightSourceData.pop_back();
275 lightSourceDesc.pop_back();
276 duryBit.resize((lightSourceData.size()+32u-1u)/32u);
277 } else {
278 lightSourceDesc[id].setRange(0);
279 lightSourceData[id] = LightSsbo();
280 freeList.push_back(id);
281 }
282 }
283
284void LightGroup::markAsDurty(size_t id) {
285 std::lock_guard<std::mutex> guard(sync);
286 markAsDurtyNoSync(id);
287 }
288
289void LightGroup::markAsDurtyNoSync(size_t id) {
290 duryBit[id/32] |= (1u << (id%32));
291 }
292
293void LightGroup::resetDurty() {
294 std::memset(duryBit.data(), 0, duryBit.size()*sizeof(duryBit[0]));
295 }
296
297const zenkit::LightPreset& LightGroup::findPreset(std::string_view preset) const {
298 for(auto& i:presets) {
299 if(i.preset!=preset)
300 continue;
301 return i;
302 }
303 Log::e("unknown light preset: \"",preset,"\"");
304 static zenkit::LightPreset zero {};
305 return zero;
306 }
307
308void LightGroup::tick(uint64_t time) {
309 for(size_t i : animatedLights) {
310 auto& light = lightSourceDesc[i];
311 light.update(time);
312
313 LightSsbo ssbo;
314 ssbo.pos = light.position();
315 ssbo.color = light.currentColor();
316 ssbo.range = light.isEnabled() ? clampRange(light.currentRange()) : 0;
317
318 auto& dst = lightSourceData[i];
319 if(std::memcmp(&dst, &ssbo, sizeof(ssbo))==0)
320 continue;
321 dst = ssbo;
322 markAsDurtyNoSync(i);
323 }
324 }
325
327 auto& device = Resources::device();
328
329 if(lightSourceSsbo.byteSize()<lightSourceData.size()*sizeof(LightSsbo)) {
330 Resources::recycle(std::move(lightSourceSsbo));
331 lightSourceSsbo = device.ssbo(lightSourceData);
332 resetDurty();
333 return true;
334 }
335 return false;
336 }
337
338void LightGroup::prepareGlobals(Tempest::Encoder<Tempest::CommandBuffer>& cmd, uint8_t fId) {
339 std::vector<Path> patchBlock;
340 std::vector<LightSsbo> patchData;
341
342 for(size_t i=0; i<lightSourceDesc.size(); ++i) {
343 if(i%32==0 && duryBit[i/32]==0) {
344 i+=31;
345 continue;
346 }
347 if((duryBit[i/32] & (1u<<i%32))==0)
348 continue;
349
350 patchData.push_back(lightSourceData[i]);
351
352 Path p;
353 p.dst = uint32_t(i);
354 p.src = uint32_t(patchData.size()-1);
355 p.size = 1;
356 if(patchBlock.size()>0) {
357 auto& b = patchBlock.back();
358 const uint32_t maxBlockSize = 16;
359 if(b.dst+b.size==p.dst && b.size<maxBlockSize) {
360 b.size++;
361 continue;
362 }
363 }
364 patchBlock.push_back(p);
365 }
366
367 if(patchBlock.empty())
368 return;
369 resetDurty();
370
371 const size_t headerSize = patchBlock.size()*sizeof(Path);
372 const size_t dataSize = patchData .size()*sizeof(LightSsbo);
373 for(auto& i:patchBlock) {
374 i.dst *= uint32_t(sizeof(LightSsbo));
375 i.src *= uint32_t(sizeof(LightSsbo));
376 i.size *= uint32_t(sizeof(LightSsbo));
377
378 i.src += uint32_t(headerSize);
379
380 // uint's in shader
381 i.dst /= sizeof(uint32_t);
382 i.src /= sizeof(uint32_t);
383 i.size /= sizeof(uint32_t);
384 }
385
386 auto& device = Resources::device();
387 auto& patch = patchSsbo[fId];
388 if(patch.byteSize()<headerSize+dataSize) {
389 Resources::recycle(std::move(patch));
390 patch = device.ssbo(Tempest::BufferHeap::Upload, Tempest::Uninitialized, headerSize+dataSize);
391 }
392 patch.update(patchBlock.data(), 0, headerSize);
393 patch.update(patchData.data(), headerSize, dataSize);
394
395 cmd.setFramebuffer({});
396 cmd.setBinding(0, lightSourceSsbo);
397 cmd.setBinding(1, patch);
398 cmd.setPipeline(Shaders::inst().patch);
399 cmd.dispatch(patchBlock.size());
400 }
void drawText(int x, int y, std::string_view txt)
void drawLine(const Tempest::Vec3 &a, const Tempest::Vec3 &b)
void setPen(const Tempest::Pen &pen)
static Gothic & inst()
Definition gothic.cpp:249
uint64_t effectPrefferedTime() const
Light & operator=(Light &&other)
void setColor(const Tempest::Vec3 &c)
void setEnabled(bool e)
void setRange(float r)
void setTimeOffset(uint64_t t)
void setPosition(float x, float y, float z)
bool updateLights()
Light add(const zenkit::LightPreset &vob)
void prepareGlobals(Tempest::Encoder< Tempest::CommandBuffer > &cmd, uint8_t fId)
LightGroup(const SceneGlobals &scene)
void dbgLights(DbgPainter &p) const
void tick(uint64_t time)
void setDebugName(std::string_view hint)
const Tempest::Vec3 & position() const
Definition lightsource.h:23
float range() const
Definition lightsource.h:25
const Tempest::Vec3 & color() const
Definition lightsource.h:16
bool isDynamic() const
bool isEnabled() const
void setColor(const Tempest::Vec3 &cl)
void setPosition(const Tempest::Vec3 &p)
Definition lightsource.h:22
void setRange(float r)
static auto openReader(std::string_view name, std::unique_ptr< zenkit::Read > &read) -> std::unique_ptr< zenkit::ReadArchive >
static Tempest::Device & device()
Definition resources.h:83
static void recycle(Tempest::DescriptorArray &&arr)
static Shaders & inst()
Definition shaders.cpp:39
static float clampRange(float r)