4 import iv
.glbinds
.utils
;
12 // ////////////////////////////////////////////////////////////////////////// //
13 public __gshared
bool vox_atlas_cache_colors
= true;
16 // ////////////////////////////////////////////////////////////////////////// //
21 pure nothrow @safe @nogc {
22 static Rect
Invalid () { pragma(inline
, true); return Rect
.init
; }
23 bool isValid () const { pragma(inline
, true); return (x
>= 0 && y
>= 0 && w
> 0 && h
> 0); }
24 int getArea () const { pragma(inline
, true); return w
*h
; }
29 int imgWidth
, imgHeight
;
32 enum BadRect
= uint.max
;
36 delete rects
; rects
= null;
37 imgWidth
= imgHeight
= 0;
40 void setSize (int awdt
, int ahgt
) {
42 assert(awdt
> 0 && ahgt
> 0);
45 rects
.assumeSafeAppend
;
46 rects
~= Rect(0, 0, awdt
, ahgt
); // one big rect
49 @property const pure nothrow @safe @nogc {
50 int width () { pragma(inline
, true); return imgWidth
; }
51 int height () { pragma(inline
, true); return imgHeight
; }
54 void dumpRects () const nothrow @trusted @nogc {
55 import core
.stdc
.stdio
: fprintf
, stderr
;
56 fprintf(stderr
, "=== ATLAS: %ux%u (%u rects) ===\n", imgWidth
, imgHeight
, cast(uint)rects
.length
);
57 for (uint f
= 0; f
< cast(uint)rects
.length
; ++f
) {
58 fprintf(stderr
, " %4u: (%d,%d)-(%d,%d)\n", rects
[f
].x
, rects
[f
].y
,
59 rects
[f
].x
+rects
[f
].w
-1, rects
[f
].y
+rects
[f
].h
-1);
61 fprintf(stderr
, "-----------\n");
65 private uint findBestFit (int w
, int h
) nothrow @trusted @nogc {
66 uint fitW
= BadRect
, fitH
= BadRect
, biggest
= BadRect
;
68 foreach (immutable idx
, const ref r
; rects
) {
69 if (r
.w
< w || r
.h
< h
) continue; // absolutely can't fit
70 if (r
.w
== w
&& r
.h
== h
) return cast(uint)idx
; // perfect fit
73 if (fitW
== BadRect || rects
.ptr
[fitW
].h
< r
.h
) fitW
= cast(uint)idx
;
74 } else if (r
.h
== h
) {
76 if (fitH
== BadRect || rects
.ptr
[fitH
].w
< r
.w
) fitH
= cast(uint)idx
;
79 if (biggest
== BadRect || rects
.ptr
[biggest
].getArea() > r
.getArea()) biggest
= cast(uint)idx
;
83 if (fitW
!= BadRect
&& fitH
!= BadRect
) return (rects
.ptr
[fitW
].getArea() > rects
.ptr
[fitH
].getArea() ? fitW
: fitH
);
84 if (fitW
!= BadRect
) return fitW
;
85 if (fitH
!= BadRect
) return fitH
;
89 // returns invalid rect if there's no room
90 Rect
insert (int cwdt
, int chgt
/*, const(uint)[] colors*/) {
91 assert(cwdt
> 0 && chgt
> 0);
92 auto ri
= findBestFit(cwdt
, chgt
);
93 if (ri
== BadRect
) return Rect
.Invalid
;
94 auto rc
= rects
.ptr
[ri
];
95 auto res
= Rect(rc
.x
, rc
.y
, cwdt
, chgt
);
97 if (rc
.w
== res
.w
&& rc
.h
== res
.h
) {
98 // best fit, simply remove this rect
99 foreach (immutable cidx
; ri
+1..rects
.length
) rects
.ptr
[cidx
-1] = rects
.ptr
[cidx
];
101 rects
.assumeSafeAppend
; // for future; we probably won't have alot of best-fitting nodes initially
107 } else if (rc
.h
== res
.h
) {
108 // split horizontally
113 // split in both directions (by longer edge)
114 if (rc
.w
-res
.w
> rc
.h
-res
.h
) {
115 // cut the right part
118 // cut the bottom part
123 // cut the bottom part
126 // cut the right part
131 rects
.assumeSafeAppend
;
141 // ////////////////////////////////////////////////////////////////////////// //
142 // just a compact representation of a rectange
143 // splitted to two copy-pasted structs for better type checking
144 align(1) struct VoxXY16
{
146 uint xy
; // low word: x; high word: y
148 nothrow @safe @nogc {
149 this (uint x
, uint y
) { pragma(inline
, true); xy
= (y
<<16)|
(x
&0xffffU
); }
150 uint getX () const pure { pragma(inline
, true); return (xy
&0xffffU
); }
151 uint getY () const pure { pragma(inline
, true); return (xy
>>16); }
152 void setX (uint x
) { pragma(inline
, true); xy
= (xy
&0xffff0000U
)|
(x
&0xffffU
); }
153 void setY (uint y
) { pragma(inline
, true); xy
= (xy
&0x0000ffffU
)|
(y
<<16); }
158 align(1) struct VoxWH16
{
160 uint wh
; // low word: x; high word: y
162 nothrow @safe @nogc {
163 this (uint w
, uint h
) { pragma(inline
, true); wh
= (h
<<16)|
(w
&0xffffU
); }
164 uint getW () const pure { pragma(inline
, true); return (wh
&0xffffU
); }
165 uint getH () const pure { pragma(inline
, true); return (wh
>>16); }
166 void setW (uint w
) { pragma(inline
, true); wh
= (wh
&0xffff0000U
)|
(w
&0xffffU
); }
167 void setH (uint h
) { pragma(inline
, true); wh
= (wh
&0x0000ffffU
)|
(h
<<16); }
172 // ////////////////////////////////////////////////////////////////////////// //
173 // color atlas, ready to be uploaded to the GPU
174 struct VoxColorPack
{
176 static struct ColorItem
{
177 VoxXY16 xy
; // start position
179 VoxXY16 newxy
; // used in relayouter
180 int next
; // -1: no more
185 uint[] colors
; // clrwdt by clrhgt
188 int[uint] citemhash
; // key: color index; value: index in `citems`
193 uint getWidth () const pure nothrow @safe @nogc { pragma(inline
, true); return clrwdt
; }
194 uint getHeight () const pure nothrow @safe @nogc { pragma(inline
, true); return clrhgt
; }
196 uint getTexX (uint cidx
) const pure nothrow @safe @nogc {
197 pragma(inline
, true);
198 return citems
[cidx
].xy
.getX();
201 uint getTexY (uint cidx
) const pure nothrow @safe @nogc {
202 pragma(inline
, true);
203 return citems
[cidx
].xy
.getY();
208 delete colors
; colors
= null;
209 delete citems
; citems
= null;
216 // prepare for new run
222 // grow image, and relayout everything
223 void growImage (uint inswdt
, uint inshgt
) {
224 uint neww
= clrwdt
, newh
= clrhgt
;
225 while (neww
< inswdt
) neww
<<= 1;
226 while (newh
< inshgt
) newh
<<= 1;
228 if (neww
< newh
) neww
<<= 1; else newh
<<= 1;
231 atlas
.setSize(neww
, newh
);
232 for (int f
= 0; f
< cast(int)citems
.length
; ++f
) {
233 ColorItem
*ci
= &citems
[f
];
234 auto rc
= atlas
.insert(cast(int)ci
.wh
.getW(), cast(int)ci
.wh
.getH());
241 ci
.newxy
= VoxXY16(rc
.x
, rc
.y
);
243 if (!again
) break; // done
246 // allocate new image, copy old data
247 conwriteln("ATLAS: resized from ", clrwdt
, "x", clrhgt
, " to ", neww
, "x", newh
);
248 uint[] newclr
= new uint[neww
*newh
];
250 for (int f
= 0; f
< cast(int)citems
.length
; ++f
) {
251 ColorItem
*ci
= &citems
[f
];
252 const uint rcw
= ci
.wh
.getW();
253 uint oaddr
= ci
.xy
.getY()*clrwdt
+ci
.xy
.getX();
254 uint naddr
= ci
.newxy
.getY()*neww
+ci
.newxy
.getX();
255 uint dy
= ci
.wh
.getH();
257 conwriteln(": : : oldpos=(", ci.rc.getX(), ",", ci.rc.getY(), "); newpos=(", newx, ",",
258 newy, "); size=(", rcw, "x", ci.rc.getH(), "); oaddr=", oaddr, "; naddr=", naddr);
261 newclr
[naddr
..naddr
+rcw
] = colors
[oaddr
..oaddr
+rcw
];
275 // returns true if found, and sets `*cidxp` and `*xyofsp`
276 // `*xyofsp` is offset inside `cidxp`
277 bool findRectEx (const(uint)[] clrs
, uint cwdt
, uint chgt
, uint cxofs
, uint cyofs
,
278 uint wdt
, uint hgt
, uint *cidxp
, VoxWH16
*whp
)
280 assert(wdt
> 0 && hgt
> 0);
281 assert(cwdt
>= wdt
&& chgt
>= hgt
);
283 const uint saddrOrig
= cyofs
*cwdt
+cxofs
;
284 auto cp
= clrs
[saddrOrig
] in citemhash
;
285 if (!cp
) return false;
287 for (int cidx
= *cp
; cidx
>= 0; cidx
= citems
[cidx
].next
) {
288 const ColorItem
*ci
= &citems
[cidx
];
289 if (wdt
> ci
.wh
.getW() || hgt
> ci
.wh
.getH()) continue; // impossibiru
292 uint saddr
= saddrOrig
;
293 uint caddr
= ci
.xy
.getY()*clrwdt
+ci
.xy
.getX();
294 for (uint dy
= 0; dy
< hgt
; ++dy
) {
295 if (colors
[caddr
..caddr
+wdt
] != clrs
[saddr
..saddr
+wdt
]) {
305 if (cidxp
!is null) *cidxp
= cast(uint)cidx
;
306 if (whp
!is null) *whp
= VoxWH16(wdt
, hgt
);
315 bool findRect (const(uint)[] clrs
, uint wdt
, uint hgt
, uint *cidxp
, VoxWH16
*whp
) {
316 pragma(inline
, true);
317 return (vox_atlas_cache_colors ?
findRectEx(clrs
, wdt
, hgt
, 0, 0, wdt
, hgt
, cidxp
, whp
) : false);
321 // returns index in `citems`
322 uint addNewRect (const(uint)[] clrs
, uint wdt
, uint hgt
) {
323 assert(wdt
> 0 && hgt
> 0);
330 while (clrwdt
< wdt
) clrwdt
<<= 1;
332 while (clrhgt
< hgt
) clrhgt
<<= 1;
333 if (clrhgt
< clrwdt
) clrhgt
= clrwdt
; //!!
334 //clrwdt = clrhgt = 1024;
335 atlas
.setSize(clrwdt
, clrhgt
);
336 colors
.length
= clrwdt
*clrhgt
;
340 // insert into atlas; grow texture if cannot insert
343 auto rc
= atlas
.insert(cast(int)wdt
, cast(int)hgt
);
345 coord
= VoxXY16(rc
.x
, rc
.y
);
348 // no room, grow the texture, and relayout everything
354 conwriteln("**ATLAS: coord=(", coord.getX(), ",", coord.getY(), ")-(",
355 coord.getX()+wdt-1, "x", coord.getY()+hgt-1, ")");
358 // copy source colors into the atlas image
360 uint daddr
= coord
.getY()*clrwdt
+coord
.getX();
361 for (uint dy
= 0; dy
< hgt
; ++dy
) {
362 foreach (uint b
; colors
[daddr
..daddr
+wdt
]) assert(b
== 0);
363 colors
[daddr
..daddr
+wdt
] = clrs
[saddr
..saddr
+wdt
];
371 ci
.wh
= VoxWH16(wdt
, hgt
);
372 const int parentIdx
= cast(int)citems
.length
;
373 if (vox_atlas_cache_colors
) {
375 auto cpp
= cc
in citemhash
;
381 citemhash
[cc
] = parentIdx
;
384 citems
.assumeSafeAppend
;
387 return cast(uint)parentIdx
;