#pragma once #include "level_format.h" #include #include #include #include #include struct LevelLighting { LightingSettings settings; std::vector lights; std::array rgb{}; // 00RRGGBB, baked static contribution + ambient LevelLighting() { rgb.fill(0x282828); } }; inline bool ValidateLighting(const LightingSettings& s, const EditorLightSource* lights, size_t count, std::string& error) { if (s.mode>2 || s.ambient>255 || s.flashlight>1 || s.flashlightRadius<32 || s.flashlightRadius>2048 || s.flashlightIntensity>255 || s.flashlightCone<1 || s.flashlightCone>96 || s.baked!=1 || s.reserved || count>MAX_EDITOR_LIGHTS) { error="Неверные параметры освещения"; return false; } for(size_t i=0;i=2048 || l.y<0 || l.y>=2048 || l.radius<1 || l.radius>2048 || l.intensity<0 || l.intensity>255 || l.curIntensity<0 || l.curIntensity>255 || (l.lightType!=LIGHT_TYPE_STATIC && l.lightType!=LIGHT_TYPE_POINT && l.lightType!=LIGHT_TYPE_PULSE) || (l.flags&~7u) || (l.color&0xFF000000)) { error="Неверные координаты, цвет или параметры источника света"; return false; } } return true; } inline bool LightingWalkable(uint8_t cell) { return cell==0 || cell==6 || cell==10; } // Exact supercover grid traversal; corner crossings cannot leak diagonally through walls. inline bool LightingVisible(const uint8_t* cells,int x1,int y1,int x2,int y2) { if(x1<0||x1>=2048||y1<0||y1>=2048||x2<0||x2>=2048||y2<0||y2>=2048) return false; int cx=x1/64,cy=y1/64,ex=x2/64,ey=y2/64; const int dx=std::abs(x2-x1),dy=std::abs(y2-y1),sx=x2>=x1?1:-1,sy=y2>=y1?1:-1; int nx=sx>0?(cx+1)*64-x1:x1-cx*64,ny=sy>0?(cy+1)*64-y1:y1-cy*64; auto free=[&](int x,int y){return x>=0&&x<32&&y>=0&&y<32&&LightingWalkable(cells[y*32+x]);}; // A wall-mounted source may start in a wall; every subsequent cell is tested. for(int steps=0;steps<65;++steps) { if(cx==ex&&cy==ey) return free(cx,cy); const int a=dx?nx*dy:0x3FFFFFFF,b=dy?ny*dx:0x3FFFFFFF; if(a==b) { if(!free(cx+sx,cy)||!free(cx,cy+sy)) return false; cx+=sx;cy+=sy;nx+=64;ny+=64; } else if(a BakeLightingRGB(const uint8_t* cells,const std::vector& lights,int ambient) { std::array rgb; const uint32_t base=ambient*0x010101u;rgb.fill(base); for(int i=0;i<1024;++i) { if(!LightingWalkable(cells[i])) continue; int x=(i%32)*64+32,y=(i/32)*64+32,r=ambient,g=ambient,b=ambient; for(const auto& l:lights) { if(!(l.flags&LIGHT_FLAG_ACTIVE)||l.lightType!=LIGHT_TYPE_STATIC||l.radius<=0) continue; int d=LightDistance(x-l.x,y-l.y);if(d>=l.radius) continue; if((l.flags&LIGHT_FLAG_SHADOWS)&&!LightingVisible(cells,l.x,l.y,x,y)) continue; int a=(l.radius-d)*l.intensity/l.radius; r=(std::min)(255,r+a*int((l.color>>16)&255)/255); g=(std::min)(255,g+a*int((l.color>>8)&255)/255); b=(std::min)(255,b+a*int(l.color&255)/255); } rgb[i]=(r<<16)|(g<<8)|b; } auto smooth=rgb; for(int y=0;y<32;++y)for(int x=0;x<32;++x) { int i=y*32+x;if(!LightingWalkable(cells[i])) continue; int r=((rgb[i]>>16)&255)*2,g=((rgb[i]>>8)&255)*2,b=(rgb[i]&255)*2,n=2; for(auto delta: {std::pair{-1,0},{1,0},{0,-1},{0,1}}) { int xx=x+delta.first,yy=y+delta.second; if(xx<0||xx>=32||yy<0||yy>=32||!LightingWalkable(cells[yy*32+xx])) continue; uint32_t c=rgb[yy*32+xx];r+=(c>>16)&255;g+=(c>>8)&255;b+=c&255;++n; } smooth[i]=((r/n)<<16)|((g/n)<<8)|(b/n); } return smooth; } inline uint64_t LightingBakeSignature(const uint8_t* cells,const std::vector& lights,int ambient) { uint64_t hash=1469598103934665603ull; auto add=[&](uint32_t v){hash^=v;hash*=1099511628211ull;}; for(int i=0;i<1024;++i)add(cells[i]);add(ambient); for(const auto& l:lights)if(l.lightType==LIGHT_TYPE_STATIC) { add(l.x);add(l.y);add(l.radius);add(l.intensity);add(l.flags&3);add(l.color); } return hash; }