4 import iv
.glbinds
.utils
;
12 // ////////////////////////////////////////////////////////////////////////// //
13 public __gshared
bool vox_atlas_cache_colors
= true;
16 // ////////////////////////////////////////////////////////////////////////// //
23 pure nothrow @safe @nogc {
24 static Rect
Invalid () { pragma(inline
, true); return Rect
.init
; }
25 bool isValid () const { pragma(inline
, true); return (x
>= 0 && y
>= 0 && w
> 0 && h
> 0); }
26 int getArea () const { pragma(inline
, true); return w
*h
; }
27 int getX1 () const { pragma(inline
, true); return x
+w
; }
28 int getY1 () const { pragma(inline
, true); return y
+h
; }
33 int imgWidth
, imgHeight
;
36 enum BadRect
= uint.max
;
40 delete rects
; rects
= null;
41 imgWidth
= imgHeight
= 0;
44 void setSize (int awdt
, int ahgt
) {
46 assert(awdt
> 0 && ahgt
> 0);
49 rects
~= Rect(0, 0, awdt
, ahgt
); // one big rect
50 rects
.assumeSafeAppend
;
53 @property const pure nothrow @safe @nogc {
54 int width () { pragma(inline
, true); return imgWidth
; }
55 int height () { pragma(inline
, true); return imgHeight
; }
59 private uint findBestFit (int w
, int h
) nothrow @trusted @nogc {
60 uint fitW
= BadRect
, fitH
= BadRect
, biggest
= BadRect
;
62 foreach (immutable idx
, const ref r
; rects
) {
63 if (r
.w
< w || r
.h
< h
) continue; // absolutely can't fit
64 if (r
.w
== w
&& r
.h
== h
) return cast(uint)idx
; // perfect fit
67 if (fitW
== BadRect || rects
.ptr
[fitW
].h
< r
.h
) fitW
= cast(uint)idx
;
68 } else if (r
.h
== h
) {
70 if (fitH
== BadRect || rects
.ptr
[fitH
].w
< r
.w
) fitH
= cast(uint)idx
;
73 if (biggest
== BadRect || rects
.ptr
[biggest
].getArea() > r
.getArea()) biggest
= cast(uint)idx
;
77 if (fitW
!= BadRect
&& fitH
!= BadRect
) return (rects
.ptr
[fitW
].getArea() > rects
.ptr
[fitH
].getArea() ? fitW
: fitH
);
78 if (fitW
!= BadRect
) return fitW
;
79 if (fitH
!= BadRect
) return fitH
;
83 // returns invalid rect if there's no room
84 Rect
insert (int cwdt
, int chgt
/*, const(uint)[] colors*/) {
85 assert(cwdt
> 0 && chgt
> 0);
86 auto ri
= findBestFit(cwdt
, chgt
);
87 if (ri
== BadRect
) return Rect
.Invalid
;
88 auto rc
= rects
.ptr
[ri
];
89 auto res
= Rect(rc
.x
, rc
.y
, cwdt
, chgt
);
91 if (rc
.w
== res
.w
&& rc
.h
== res
.h
) {
92 // best fit, simply remove this rect
93 foreach (immutable cidx
; ri
+1..rects
.length
) rects
.ptr
[cidx
-1] = rects
.ptr
[cidx
];
95 rects
.assumeSafeAppend
; // for future; we probably won't have alot of best-fitting nodes initially
101 } else if (rc
.h
== res
.h
) {
102 // split horizontally
107 // split in both directions (by longer edge)
108 if (rc
.w
-res
.w
> rc
.h
-res
.h
) {
109 // cut the right part
112 // cut the bottom part
117 // cut the bottom part
120 // cut the right part
126 rects
.assumeSafeAppend
;
135 // ////////////////////////////////////////////////////////////////////////// //
136 // just a compact representation of a rectange
137 // splitted to two copy-pasted structs for better type checking
138 align(1) struct VoxXY16
{
140 uint xy
; // low word: x; high word: y
142 nothrow @safe @nogc {
143 this (uint x
, uint y
) { pragma(inline
, true); xy
= (y
<<16)|
(x
&0xffffU
); }
144 uint getX () const pure { pragma(inline
, true); return (xy
&0xffffU
); }
145 uint getY () const pure { pragma(inline
, true); return (xy
>>16); }
146 void setX (uint x
) { pragma(inline
, true); xy
= (xy
&0xffff0000U
)|
(x
&0xffffU
); }
147 void setY (uint y
) { pragma(inline
, true); xy
= (xy
&0x0000ffffU
)|
(y
<<16); }
152 align(1) struct VoxWH16
{
154 uint wh
; // low word: x; high word: y
156 nothrow @safe @nogc {
157 this (uint w
, uint h
) { pragma(inline
, true); wh
= (h
<<16)|
(w
&0xffffU
); }
158 uint getW () const pure { pragma(inline
, true); return (wh
&0xffffU
); }
159 uint getH () const pure { pragma(inline
, true); return (wh
>>16); }
160 void setW (uint w
) { pragma(inline
, true); wh
= (wh
&0xffff0000U
)|
(w
&0xffffU
); }
161 void setH (uint h
) { pragma(inline
, true); wh
= (wh
&0x0000ffffU
)|
(h
<<16); }
166 // ////////////////////////////////////////////////////////////////////////// //
167 // color atlas, ready to be uploaded to the GPU
168 struct VoxColorPack
{
170 static struct ColorItem
{
171 VoxXY16 xy
; // start position
173 VoxXY16 newxy
; // used in relayouter
174 int next
; // -1: no more
179 uint[] colors
; // clrwdt by clrhgt
182 int[uint] citemhash
; // key: color index; value: index in `citems`
187 uint getWidth () const pure nothrow @safe @nogc { pragma(inline
, true); return clrwdt
; }
188 uint getHeight () const pure nothrow @safe @nogc { pragma(inline
, true); return clrhgt
; }
190 uint getTexX (uint cidx
) const pure nothrow @safe @nogc {
191 pragma(inline
, true);
192 return citems
[cidx
].xy
.getX();
195 uint getTexY (uint cidx
) const pure nothrow @safe @nogc {
196 pragma(inline
, true);
197 return citems
[cidx
].xy
.getY();
202 delete colors
; colors
= null;
203 delete citems
; citems
= null;
209 // prepare for new run
215 // grow image, and relayout everything
216 void growImage (uint inswdt
, uint inshgt
) {
217 uint neww
= clrwdt
, newh
= clrhgt
;
218 while (neww
< inswdt
) neww
<<= 1;
219 while (newh
< inshgt
) newh
<<= 1;
221 if (neww
< newh
) neww
<<= 1; else newh
<<= 1;
224 atlas
.setSize(neww
, newh
);
225 for (int f
= 0; f
< cast(int)citems
.length
; ++f
) {
226 ColorItem
*ci
= &citems
[f
];
227 auto rc
= atlas
.insert(cast(int)ci
.wh
.getW(), cast(int)ci
.wh
.getH());
234 ci
.newxy
= VoxXY16(rc
.x
, rc
.y
);
236 if (!again
) break; // done
239 // allocate new image, copy old data
240 conwriteln("ATLAS: resized from ", clrwdt
, "x", clrhgt
, " to ", neww
, "x", newh
);
241 uint[] newclr
= new uint[neww
*newh
];
243 for (int f
= 0; f
< cast(int)citems
.length
; ++f
) {
244 ColorItem
*ci
= &citems
[f
];
245 const uint rcw
= ci
.wh
.getW();
246 uint oaddr
= ci
.xy
.getY()*clrwdt
+ci
.xy
.getX();
247 uint naddr
= ci
.newxy
.getY()*neww
+ci
.newxy
.getX();
248 uint dy
= ci
.wh
.getH();
250 conwriteln(": : : oldpos=(", ci.rc.getX(), ",", ci.rc.getY(), "); newpos=(", newx, ",",
251 newy, "); size=(", rcw, "x", ci.rc.getH(), "); oaddr=", oaddr, "; naddr=", naddr);
254 newclr
[naddr
..naddr
+rcw
] = colors
[oaddr
..oaddr
+rcw
];
268 // returns true if found, and sets `*cidxp` and `*xyofsp`
269 // `*xyofsp` is offset inside `cidxp`
270 bool findRectEx (const(uint)[] clrs
, uint cwdt
, uint chgt
, uint cxofs
, uint cyofs
,
271 uint wdt
, uint hgt
, uint *cidxp
, VoxWH16
*whp
)
273 assert(wdt
> 0 && hgt
> 0);
274 assert(cwdt
>= wdt
&& chgt
>= hgt
);
276 const uint saddrOrig
= cyofs
*cwdt
+cxofs
;
277 auto cp
= clrs
[saddrOrig
] in citemhash
;
278 if (!cp
) return false;
280 for (int cidx
= *cp
; cidx
>= 0; cidx
= citems
[cidx
].next
) {
281 const ColorItem
*ci
= &citems
[cidx
];
282 if (wdt
> ci
.wh
.getW() || hgt
> ci
.wh
.getH()) continue; // impossibiru
285 uint saddr
= saddrOrig
;
286 uint caddr
= ci
.xy
.getY()*clrwdt
+ci
.xy
.getX();
287 for (uint dy
= 0; dy
< hgt
; ++dy
) {
288 if (colors
[caddr
..caddr
+wdt
] != clrs
[saddr
..saddr
+wdt
]) {
298 if (cidxp
!is null) *cidxp
= cast(uint)cidx
;
299 if (whp
!is null) *whp
= VoxWH16(wdt
, hgt
);
308 bool findRect (const(uint)[] clrs
, uint wdt
, uint hgt
, uint *cidxp
, VoxWH16
*whp
) {
309 pragma(inline
, true);
310 return (vox_atlas_cache_colors ?
findRectEx(clrs
, wdt
, hgt
, 0, 0, wdt
, hgt
, cidxp
, whp
) : false);
314 // returns index in `citems`
315 uint addNewRect (const(uint)[] clrs
, uint wdt
, uint hgt
) {
316 assert(wdt
> 0 && hgt
> 0);
323 while (clrwdt
< wdt
) clrwdt
<<= 1;
325 while (clrhgt
< hgt
) clrhgt
<<= 1;
326 if (clrhgt
< clrwdt
) clrhgt
= clrwdt
; //!!
327 atlas
.setSize(clrwdt
, clrhgt
);
328 coord
= VoxXY16(0, 0);
329 colors
.length
= clrwdt
*clrhgt
;
332 // insert into atlas; grow texture if cannot insert
334 auto rc
= atlas
.insert(cast(int)wdt
, cast(int)hgt
);
336 coord
= VoxXY16(rc
.x
, rc
.y
);
339 // no room, grow the texture, and relayout everything
344 // copy source colors into the atlas image
346 uint daddr
= coord
.getY()*clrwdt
+coord
.getX();
347 for (uint dy
= 0; dy
< hgt
; ++dy
) {
348 colors
[daddr
..daddr
+wdt
] = clrs
[saddr
..saddr
+wdt
];
356 ci
.wh
= VoxWH16(wdt
, hgt
);
357 const int parentIdx
= cast(int)citems
.length
;
358 if (vox_atlas_cache_colors
) {
360 auto cpp
= cc
in citemhash
;
366 citemhash
[cc
] = parentIdx
;
370 citems
.assumeSafeAppend
;
372 return cast(uint)parentIdx
;