2 * Pixel Graphics Library
3 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
4 * Understanding is not required. Only obedience.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 // ////////////////////////////////////////////////////////////////////////// //
20 import core
.time
: MonoTime
, Duration
;
22 static if (__traits(compiles
, () { import arsd
.simpledisplay
; })) {
23 public import arsd
.simpledisplay
;
25 public import simpledisplay
;
28 __gshared SimpleWindow sdwindow
;
29 __gshared Image texImage
;
31 shared static ~this () {
38 enum scanlines
= false;
40 enum vlEffectiveWidth
= vlWidth
*scale
;
41 enum vlEffectiveHeight
= vlHeight
*scale
;
44 // ////////////////////////////////////////////////////////////////////////// //
45 ubyte clampToByte(T
) (T n
) pure nothrow @safe @nogc if (__traits(isIntegral
, T
)) {
46 //static assert(!__traits(isUnsigned, T), "clampToByte can't process unsigned types");
47 static if (__VERSION__
> 2067) pragma(inline
, true);
48 static if (T
.sizeof
== 2 || T
.sizeof
== 4) {
49 static if (__traits(isUnsigned
, T
)) {
50 return cast(ubyte)(n
&0xff|
(255-((-cast(int)(n
< 256))>>24)));
52 n
&= -cast(int)(n
>= 0);
53 return cast(ubyte)(n|
((255-cast(int)n
)>>31));
55 } else static if (T
.sizeof
== 1) {
56 static assert(__traits(isUnsigned
, T
), "clampToByte: signed byte? no, really?");
59 static assert(false, "clampToByte: integer too big");
64 // ////////////////////////////////////////////////////////////////////////// //
65 immutable ubyte[1730] iconDisk
= [
66 0x18,0x18,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x2f,0x23,0x13,0x27,0x1f,0x17,0x27,0x1f,0x17,
67 0x27,0x1f,0x17,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1f,0x17,0x27,0x1b,0x0f,0x27,0x1f,0x17,0x27,0x1f,
68 0x17,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1f,0x17,0x27,0x1b,0x0f,0x27,0x1f,0x17,0x2f,0x23,0x13,0x27,
69 0x1f,0x17,0x27,0x1f,0x17,0x27,0x1b,0x0f,0x27,0x1f,0x17,0x27,0x1b,0x0f,0x1f,0x17,0x0b,0x37,0x2b,0x1f,
70 0x37,0x2b,0x1f,0x37,0x2b,0x1f,0x2f,0x23,0x13,0x2f,0x23,0x13,0x2f,0x23,0x13,0x2f,0x23,0x13,0x2f,0x23,
71 0x13,0x2f,0x23,0x13,0x2f,0x23,0x13,0x37,0x2b,0x1f,0x2f,0x23,0x13,0x2f,0x23,0x13,0x37,0x2b,0x1f,0x2f,
72 0x23,0x13,0x2f,0x23,0x13,0x37,0x2b,0x17,0x37,0x2b,0x1f,0x2f,0x23,0x13,0x2f,0x23,0x13,0x37,0x2b,0x17,
73 0x37,0x2b,0x1f,0x1f,0x17,0x0b,0x17,0x0f,0x0b,0x2f,0x23,0x13,0x27,0x1f,0x17,0x27,0x1f,0x17,0x27,0x1b,
74 0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1f,0x17,0x27,0x1f,0x17,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x27,
75 0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1f,0x17,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1f,0x17,0x27,0x1f,0x17,
76 0x27,0x1f,0x17,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x27,0x1f,0x17,0x27,0x1b,0x0f,0x1f,0x17,0x0b,0x17,0x0f,
77 0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,
78 0x1b,0x0f,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,
79 0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x27,0x1b,
80 0x0f,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x1f,0x17,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,
81 0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,
82 0x3b,0x1f,0x0f,0x4b,0x23,0x13,0x4b,0x23,0x13,0x4b,0x23,0x13,0x2f,0x17,0x0b,0x27,0x1b,0x0f,0x27,0x1b,
83 0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x17,
84 0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,
85 0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x57,0x2b,0x17,0x5f,0x4b,0x3b,0x7b,0x63,0x53,0x8f,0x43,0x33,0x7b,0x63,
86 0x53,0xaf,0x63,0x2f,0x9f,0x4f,0x33,0x5f,0x4b,0x3b,0x43,0x33,0x27,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,
87 0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,
88 0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x57,0x2b,0x17,0x7b,0x63,0x53,0x5f,0x4b,
89 0x3b,0x73,0x37,0x23,0x53,0x3f,0x33,0x43,0x33,0x27,0x43,0x33,0x27,0x43,0x33,0x27,0x37,0x2b,0x1f,0x53,
90 0x3f,0x33,0x6b,0x57,0x47,0x7f,0x53,0x3f,0x43,0x33,0x27,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,
91 0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,
92 0x0f,0x43,0x33,0x27,0x7f,0x53,0x3f,0x53,0x3f,0x33,0x47,0x33,0x1b,0x43,0x33,0x27,0x4b,0x23,0x13,0x1b,
93 0x13,0x0f,0x1b,0x13,0x0f,0x1b,0x13,0x0f,0x4b,0x23,0x13,0x43,0x33,0x27,0x43,0x33,0x27,0x63,0x2f,0x1f,
94 0x9f,0x4f,0x33,0x43,0x33,0x27,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,
95 0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x43,0x33,0x27,0x5f,0x4b,0x3b,0x47,0x33,0x1b,0x43,
96 0x33,0x27,0x53,0x3f,0x33,0x37,0x2b,0x1f,0x5f,0x4b,0x3b,0x4b,0x23,0x13,0x1b,0x13,0x0f,0x3b,0x1f,0x0f,
97 0x53,0x3f,0x33,0x43,0x33,0x27,0x53,0x3f,0x33,0x43,0x33,0x27,0x53,0x3f,0x33,0x7f,0x3b,0x2b,0x43,0x33,
98 0x27,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x37,
99 0x2b,0x1f,0x53,0x3f,0x33,0x3b,0x1f,0x0f,0x37,0x2b,0x1f,0x27,0x1f,0x17,0x5f,0x4b,0x3b,0x3b,0x1f,0x0f,
100 0x43,0x33,0x27,0x6b,0x57,0x47,0x4b,0x23,0x13,0x8f,0x43,0x33,0x37,0x2b,0x1f,0x27,0x1f,0x17,0x63,0x3f,
101 0x2b,0x3b,0x1f,0x0f,0x37,0x2b,0x1f,0x43,0x33,0x27,0x5f,0x4b,0x3b,0x37,0x2b,0x1f,0x27,0x1b,0x0f,0x17,
102 0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x43,0x33,0x27,0x63,0x2f,0x1f,0x53,0x3f,0x33,
103 0x27,0x1f,0x17,0x1b,0x13,0x0f,0x53,0x3f,0x33,0x1f,0x17,0x0b,0x1b,0x13,0x0f,0x53,0x3f,0x33,0x9f,0x4f,
104 0x33,0x53,0x3f,0x33,0x1b,0x13,0x0f,0x1b,0x13,0x0f,0x63,0x3f,0x2b,0x17,0x0f,0x0b,0x1b,0x13,0x0f,0x37,
105 0x2b,0x1f,0x27,0x1f,0x17,0x53,0x3f,0x33,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,
106 0x2f,0x23,0x13,0x53,0x3f,0x33,0x37,0x2b,0x1f,0x27,0x1f,0x17,0x17,0x0f,0x0b,0x37,0x2b,0x1f,0x53,0x3f,
107 0x33,0x6b,0x57,0x47,0x6f,0x47,0x33,0x27,0x1f,0x17,0x1f,0x17,0x0b,0x27,0x1f,0x17,0x5f,0x4b,0x3b,0x6b,
108 0x57,0x47,0x53,0x3f,0x33,0x37,0x2b,0x1f,0x1f,0x17,0x0b,0x3b,0x1f,0x0f,0x53,0x3f,0x33,0x53,0x3f,0x33,
109 0x1f,0x17,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x47,0x33,0x1b,0x47,0x33,
110 0x1b,0x27,0x1f,0x17,0x43,0x33,0x27,0x8b,0x5f,0x47,0x7f,0x53,0x3f,0x5f,0x4b,0x3b,0x27,0x1f,0x17,0x1b,
111 0x13,0x0f,0x1b,0x13,0x0f,0x27,0x1f,0x17,0x27,0x1f,0x17,0x53,0x3f,0x33,0x5f,0x4b,0x3b,0x8b,0x5f,0x47,
112 0x43,0x33,0x27,0x53,0x3f,0x33,0x43,0x33,0x27,0x53,0x3f,0x33,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,
113 0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x37,0x2b,0x1f,0x37,0x2b,0x13,0x5f,0x4b,0x3b,0x7b,0x63,0x53,0x63,
114 0x3f,0x2b,0x17,0x00,0x00,0x1b,0x13,0x0f,0x5f,0x4b,0x3b,0x37,0x2b,0x1f,0x1b,0x13,0x0f,0x37,0x2b,0x1f,
115 0x5f,0x4b,0x3b,0x1b,0x13,0x0f,0x1b,0x13,0x0f,0x53,0x3f,0x33,0x8b,0x5f,0x47,0x5f,0x4b,0x3b,0x43,0x33,
116 0x27,0x57,0x2b,0x17,0x2f,0x23,0x13,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x43,
117 0x33,0x27,0x37,0x2b,0x13,0x43,0x33,0x27,0x53,0x37,0x23,0x53,0x37,0x23,0x5f,0x4b,0x3b,0x6b,0x57,0x47,
118 0x53,0x3f,0x33,0x7f,0x53,0x3f,0xaf,0x63,0x2f,0x7f,0x53,0x3f,0x53,0x3f,0x33,0x6f,0x47,0x33,0x5f,0x4b,
119 0x3b,0x63,0x3f,0x2b,0x53,0x37,0x23,0x4b,0x23,0x13,0x37,0x2b,0x1f,0x43,0x33,0x27,0x27,0x1b,0x0f,0x1f,
120 0x17,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x1b,0x13,0x0f,0x37,0x2b,0x1f,0x3b,0x1f,0x0f,
121 0x63,0x3f,0x2b,0x27,0x1f,0x17,0x27,0x1f,0x17,0x27,0x1f,0x17,0x43,0x33,0x27,0x5f,0x4b,0x3b,0x1b,0x13,
122 0x0f,0x63,0x3f,0x2b,0x43,0x33,0x27,0x27,0x1f,0x17,0x27,0x1f,0x17,0x27,0x1f,0x17,0x53,0x3f,0x33,0x27,
123 0x1f,0x17,0x4b,0x23,0x13,0x3b,0x1f,0x0f,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,
124 0x2f,0x23,0x13,0x0f,0x0b,0x07,0x3b,0x1f,0x0f,0x2f,0x17,0x0b,0x43,0x33,0x27,0x6f,0x47,0x33,0x1b,0x13,
125 0x0f,0x1b,0x13,0x0f,0x3b,0x1f,0x0f,0x53,0x3f,0x33,0x1b,0x13,0x0f,0x43,0x33,0x27,0x3b,0x1f,0x0f,0x1b,
126 0x13,0x0f,0x1b,0x13,0x0f,0x6f,0x47,0x33,0x43,0x33,0x27,0x5f,0x4b,0x3b,0x3b,0x1f,0x0f,0x1f,0x17,0x0b,
127 0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x1b,0x13,
128 0x0f,0x53,0x3f,0x33,0x43,0x33,0x27,0x43,0x33,0x27,0x7f,0x53,0x3f,0x43,0x33,0x27,0x2f,0x17,0x0b,0x43,
129 0x33,0x27,0x7b,0x63,0x53,0x43,0x33,0x27,0x2f,0x17,0x0b,0x53,0x37,0x23,0x7f,0x53,0x3f,0x43,0x33,0x27,
130 0x37,0x2b,0x13,0x37,0x2b,0x1f,0x0f,0x0b,0x07,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,
131 0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x1b,0x13,0x0f,0x43,0x33,0x27,0x27,
132 0x1f,0x17,0x43,0x33,0x27,0x7f,0x3b,0x2b,0x6b,0x57,0x47,0x53,0x3f,0x33,0x5f,0x4b,0x3b,0x53,0x3f,0x33,
133 0x6b,0x57,0x47,0x6f,0x47,0x33,0x47,0x33,0x1b,0x37,0x2b,0x13,0x27,0x1f,0x17,0x1b,0x13,0x0f,0x27,0x1b,
134 0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x1f,
135 0x17,0x0b,0x1f,0x17,0x0b,0x1f,0x17,0x0b,0x0f,0x00,0x00,0x27,0x1f,0x17,0x43,0x33,0x27,0x43,0x33,0x27,
136 0x43,0x33,0x27,0x27,0x1f,0x17,0x43,0x33,0x27,0x37,0x2b,0x1f,0x43,0x33,0x27,0x4b,0x23,0x13,0x43,0x33,
137 0x27,0x2f,0x17,0x0b,0x1b,0x13,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x17,
138 0x0f,0x0b,0x1f,0x17,0x0b,0x27,0x1b,0x0f,0x1f,0x17,0x0b,0x1f,0x17,0x0b,0x1f,0x17,0x0b,0x1f,0x17,0x0b,
139 0x1f,0x17,0x0b,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x57,0x2b,0x17,0x3b,0x1f,0x0f,0x27,0x1f,0x17,0x43,0x33,
140 0x27,0x27,0x1f,0x17,0x27,0x1f,0x17,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,
141 0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x1f,0x17,0x0b,0x27,0x1b,0x0f,
142 0x1f,0x17,0x0b,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x1f,0x17,0x0b,0x27,0x1b,0x0f,0x2f,0x23,
143 0x13,0x2f,0x23,0x13,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x2f,0x23,0x13,0x2f,
144 0x23,0x13,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,
145 0x27,0x1b,0x0f,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x1f,0x17,0x0b,0x1f,0x17,0x0b,0x2f,0x23,0x13,0x37,0x2b,
146 0x17,0x37,0x2b,0x17,0x2f,0x23,0x13,0x2f,0x23,0x13,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x1f,
147 0x17,0x0b,0x27,0x1b,0x0f,0x2f,0x23,0x13,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x27,0x1b,0x0f,
148 0x27,0x1b,0x0f,0x27,0x1b,0x0f,0x1f,0x17,0x0b,0x27,0x1b,0x0f,0x1f,0x17,0x0b,0x17,0x0f,0x0b,0x1f,0x17,
149 0x0b,0x1f,0x17,0x0b,0x1f,0x17,0x0b,0x1f,0x17,0x0b,0x1f,0x17,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,
150 0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,
151 0x1f,0x17,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,0x17,0x0f,
152 0x0b,0x1f,0x17,0x0b,0x17,0x0f,0x0b,0x17,0x0f,0x0b,];
155 auto diskp
= iconDisk
.ptr
;
158 auto nextLineAdjustment
= texImage
.adjustmentForNextLine();
159 auto offR
= texImage
.redByteOffset();
160 auto offB
= texImage
.blueByteOffset();
161 auto offG
= texImage
.greenByteOffset();
162 auto bpp
= texImage
.bytesPerPixel();
163 auto imgdata
= texImage
.getDataPointer();
164 // figure out the starting byte offset
165 auto X
= texImage
.width
-w
*scale
;
167 auto offset
= texImage
.offsetForTopLeftPixel()+nextLineAdjustment
*Y
+bpp
*X
;
168 auto startOfLine
= imgdata
+offset
; // get our pointer lined up on the first pixel
169 foreach (immutable _0
; 0..h
) {
170 auto vbptr
= startOfLine
; // we keep the start of line separately so moving to the next line is simple and portable
171 startOfLine
+= nextLineAdjustment
*scale
;
172 foreach (immutable _1
; 0..w
) {
176 foreach (immutable _
; 0..scale
) {
180 static if (!scanlines
) {
182 foreach (immutable _2
; 1..scale
) {
183 vbptrv
+= nextLineAdjustment
;
196 // ////////////////////////////////////////////////////////////////////////// //
198 import std
.format
: FormatSpec
;
199 void toString (scope void delegate(const(char)[]) sink
, FormatSpec
!char fmt
) const @trusted {
202 sink
.formatValue(x
, fmt
);
204 sink
.formatValue(y
, fmt
);
206 sink
.formatValue(z
, fmt
);
211 float x
= 0, y
= 0, z
= 0;
213 @property float opIndex (size_t idx
) const pure {
214 static if (__VERSION__
> 2067) pragma(inline
, true);
215 if (idx
> 2) assert(0, "invalid index in Vec3 opindex");
216 return (idx
== 0 ? x
: (idx
== 1 ? y
: z
));
219 @property float opIndexAssign (float v
, size_t idx
) {
220 static if (__VERSION__
> 2067) pragma(inline
, true);
221 if (idx
> 2) assert(0, "invalid index in Vec3 opindex");
223 case 0: x
= v
; break;
224 case 1: y
= v
; break;
225 case 2: z
= v
; break;
230 ref Vec3
normalize () {
231 //pragma(inline, true);
232 import std
.math
: sqrt
;
233 immutable invlen
= 1.0f/sqrt(x
*x
+y
*y
+z
*z
);
241 static float length (float x
, float y
) {
242 //pragma(inline, true);
243 import std
.math
: sqrt
;
244 return sqrt(x
*x
+y
*y
);
247 static float length (float x
, float y
, float z
) {
248 //pragma(inline, true);
249 import std
.math
: sqrt
;
250 return sqrt(x
*x
+y
*y
+z
*z
);
254 //pragma(inline, true);
255 import std
.math
: sqrt
;
256 return sqrt(x
*x
+y
*y
+z
*z
);
257 //return length(x, y, z);
261 //pragma(inline, true);
262 import std
.math
: sqrt
;
263 immutable invlen
= 1.0f/sqrt(x
*x
+y
*y
+z
*z
);
264 return Vec3(x
*invlen
, y
*invlen
, z
*invlen
);
268 //pragma(inline, true);
269 import std
.math
: abs
;
270 return Vec3(abs(x
), abs(y
), abs(z
));
273 Vec3
minmax(string op
) (in auto ref Vec3 b
) if (op
== "min" || op
== "max") {
274 //pragma(inline, true);
275 mixin("import std.algorithm : "~op
~";");
276 return mixin("Vec3("~op
~"(x, b.x), "~op
~"(y, b.y), "~op
~"(z, b.z))");
279 Vec3
minmax(string op
) (float v
) if (op
== "min" || op
== "max") {
280 //pragma(inline, true);
281 mixin("import std.algorithm : "~op
~";");
282 return mixin("Vec3("~op
~"(x, v), "~op
~"(y, v), "~op
~"(z, v))");
286 alias min = minmax!"min";
287 alias max = minmax!"max";
290 Vec3
opBinary(string op
) (in auto ref Vec3 b
) if (op
== "+" || op
== "-" || op
== "*" || op
== "/") {
291 static if (__VERSION__
> 2067) pragma(inline
, true);
292 return mixin("Vec3(x"~op
~"b.x, y"~op
~"b.y, z"~op
~"b.z)");
295 Vec3
opBinary(string op
) (in float b
) if (op
== "+" || op
== "-" || op
== "*" || op
== "/") {
296 static if (__VERSION__
> 2067) pragma(inline
, true);
297 return mixin("Vec3(x"~op
~"b, y"~op
~"b, z"~op
~"b)");
300 // cross product (normal to plane containing a and b)
301 Vec3
opBinary(string op
: "%") (in auto ref Vec3 b
) {
302 static if (__VERSION__
> 2067) pragma(inline
, true);
303 return Vec3(y
*b
.z
-z
*b
.y
, z
*b
.x
-x
*b
.z
, x
*b
.y
-y
*b
.x
);
306 float dot() (in auto ref Vec3 b
) {
307 static if (__VERSION__
> 2067) pragma(inline
, true);
308 return x
*b
.x
+y
*b
.y
+z
*b
.z
;
311 float anglev() (in auto ref Vec3 b
) {
312 static if (__VERSION__
> 2067) pragma(inline
, true);
313 import std
.math
: acos
;
314 return acos(dot(b
)/(length
*b
.length
));
317 // things like `.xyy`
318 Vec3
opDispatch(string type
) ()
319 if (type
.length
== 3 &&
320 (type
[0] == 'x' || type
[0] == 'y' || type
[0] == 'z') &&
321 (type
[1] == 'x' || type
[1] == 'y' || type
[1] == 'z') &&
322 (type
[2] == 'x' || type
[2] == 'y' || type
[2] == 'z'))
324 static if (__VERSION__
> 2067) pragma(inline
, true);
325 return mixin("Vec3("~type
[0]~","~type
[1]~","~type
[2]~")");
329 auto rotx() (float angle) {
330 //pragma(inline, true);
331 float sine = void, cosine = void;
332 asm pure nothrow @trusted @nogc {
338 return Vec3(x, y*cosine-z*sine, y*sine+z*cosine);
341 auto roty() (float angle) {
342 //pragma(inline, true);
343 float sine = void, cosine = void;
344 asm pure nothrow @trusted @nogc {
350 return Vec3(x*cosine-z*sine, y, x*sine+z*cosine);
353 auto rotz() (float angle) {
354 //pragma(inline, true);
355 float sine = void, cosine = void;
356 asm pure nothrow @trusted @nogc {
362 return Vec3(x*cosine-y*sine, x*sine+y*cosine, z);
367 ref Vec3
opOpAssign(string op
) (in auto ref Vec3 b
) if (op
== "+" || op
== "-" || op
== "*" || op
== "/") {
368 static if (__VERSION__
> 2067) pragma(inline
, true);
369 mixin("x"~op
~"=b.x;");
370 mixin("y"~op
~"=b.y;");
371 mixin("z"~op
~"=b.z;");
375 //static float smoothstep (float x) pure { static if (__VERSION__ > 2067) pragma(inline, true); return x*x*(3-2*x); }
379 // ////////////////////////////////////////////////////////////////////////// //
380 // raymarch global vars
381 __gshared
float worldtime
= 0; // seconds
382 __gshared Vec3 vrp
; // view reference point
383 __gshared Vec3 vuv
; // view up vector
384 __gshared Vec3 prp
; // camera position
387 immutable Vec3 e
= Vec3(0.02f, 0.0f, 0.0f);
388 immutable Vec3 exyy
= e
.xyy
;
389 immutable Vec3 eyxy
= e
.yxy
;
390 immutable Vec3 eyyx
= e
.yyx
;
393 float fract() (float x
) {
394 //pragma(inline, true);
395 import std
.math
: floor
;
401 float mod() (float x
, float y
) {
402 //pragma(inline, true);
403 import std
.math
: floor
;
404 // x minus the product of y and floor(x/y)
405 return x
-y
*floor(x
/y
);
409 // ////////////////////////////////////////////////////////////////////////// //
410 // raymarch composition functions
412 float objopRep(string objfunc
) (in auto ref Vec3 pp
, in auto ref Vec3 c
) {
413 static if (__VERSION__
> 2067) pragma(inline
, true);
414 // the following MUST be `p`
415 immutable auto p
= Vec3(mod(p
.x
, c
.x
), mod(p
.y
, c
.y
), mod(p
.z
, c
.z
))-c
*0.5f;
416 return mixin(objfunc
);
419 float objopScale(string objfunc
) (in auto ref Vec3 pp
, float s
) {
420 static if (__VERSION__
> 2067) pragma(inline
, true);
421 immutable auto p
= p
/s
;
422 return mixin(objfunc
)*s
;
425 // rotation/translation
427 Vec3 objopTx(string objfunc) (in auto ref Vec3 pp, in auto ref Mat4 mt) {
428 immutable auto p = m.invert*pp;
429 return mixin(objfunc);
433 float objopUnion() (float obj0
, float obj1
) {
434 static if (__VERSION__
> 2067) pragma(inline
, true);
435 return (obj0
< obj1 ? obj0
: obj1
); // min
438 float objopSub() (float a
, float b
) {
439 static if (__VERSION__
> 2067) pragma(inline
, true);
440 return (a
> -b ? a
: -b
);
444 float objopInter() (float a
, float b
) {
445 static if (__VERSION__
> 2067) pragma(inline
, true);
446 return (obj0
> obj1 ? obj0
: obj1
); // max
449 // returns object number
450 int objopUnion() (ref float dest
, float obj0
, float obj1
, int oid0
=0, int oid1
=1) {
451 static if (__VERSION__
> 2067) pragma(inline
, true);
461 // returns object number
462 int objopSub() (ref float dest
, float a
, float b
, int oid0
=0, int oid1
=1) {
463 static if (__VERSION__
> 2067) pragma(inline
, true);
474 // returns object number
475 int objopInter() (ref float dest
, float obj0
, float obj1
, int oid0
=0, int oid1
=1) {
476 static if (__VERSION__
> 2067) pragma(inline
, true);
487 // ////////////////////////////////////////////////////////////////////////// //
488 // raymarch composition functions that doesn't preserve distance
489 // you will probably need to decrease your step size, if you are using a raymarcher to sample this
490 // the displacement example below is using sin(20*p.x)*sin(20*p.y)*sin(20*p.z) as displacement pattern
492 float objopDisplace(string objfunc) (in auto ref Vec3 p) {
493 float d1 = mixin(objfunc);
494 float d2 = displacement(p);
500 // exponential smooth min (k = 32);
501 float sminExp (float a
, float b
, float k
) {
502 import std
.math
: exp
, log
;
503 float res
= exp(-k
*a
)+exp(-k
*b
);
507 // power smooth min (k = 8);
508 float sminPow (float a
, float b
, float k
) {
509 import std
.math
: pow
;
512 return pow((a
*b
)/(a
+b
), 1.0f/k
);
515 // polynomial smooth min (k = 0.1);
517 float sminPoly (float a, float b, float k) {
518 import std.math : clamp, exp, log;
519 float h = clamp(0.5f+0.5f*(b-a)/k, 0.0f, 1.0f);
520 return mix(b, a, h)-k*h*(1.0f-h);
524 float objopBlend(alias sminfn
, string objfunc0
, string objfunc1
) (in auto ref Vec3 p
) {
525 float d1
= mixin(objfunc0
);
526 float d2
= mixin(objfunc1
);
527 return sminfn(d1
, d2
);
532 float objopTwist (string objfunc) (in auto ref Vec3 pp) { {
533 import std.math : cos, sin;
534 float c = cos(20.0f*pp.y);
535 float s = sin(20.0f*pp.y);
536 immutable auto m = Mat2(c, -s, s, c);
537 immutable auto p = Vec3(m*p.xz, p.y);
538 return mixin(objfunc);
543 // ////////////////////////////////////////////////////////////////////////// //
544 import std
.algorithm
: maxf
= max
, minf
= min
;
546 // raymarch object functions
547 // as our functions are very simple, use strings and `mixin` to simulate inlining ;-)
548 enum floorHeight
= 12.0f;
549 enum objfuncFloor
= q
{(p
.y
+floorHeight
)};
551 // n must be normalized (invalid formula, broken by k8)
552 static immutable planeN
= Vec3(0.0f, 0.0f, 0.0f);
554 enum objfuncPlane
= q
{(p
.dot(planeN
)+planeD
)};
556 enum torusOuterRadius
= 2.4f;
557 enum torusRadius
= 0.6f;
558 enum objfuncTorus
= q
{(Vec3
.length(p
.length
-torusOuterRadius
, p
.z
)-torusRadius
)};
561 enum shpereRadius
= 1.9f;
562 enum objfuncSphere
= q
{(p
.length
-shpereRadius
)};
564 // round box, unsigned
565 static immutable roundboxSize
= Vec3(2.0f, 0.7f, 2.0f);
566 enum roundboxRadius
= 0.2f;
567 enum objfuncRoundbox
= q
{((p
.abs
-roundboxSize
).minmax
!"max"(0.0f).length
-roundboxRadius
)};
569 // box, signed (interestingly, this is more complex than round box ;-)
570 static immutable boxSize
= Vec3(2.0f, 0.7f, 2.0f);
571 __gshared Vec3 boxTemp
;
572 enum objfuncBox
= q
{(boxTemp
= p
.abs
-boxSize
, minf(maxf(boxTemp
.x
, maxf(boxTemp
.y
, boxTemp
.z
)), 0.0f)+boxTemp
.minmax
!"max"(0.0f).length
)};
575 static immutable boxuSize
= Vec3(2.0f, 0.7f, 2.0f);
576 enum objfuncBoxu
= q
{((p
.abs
-boxuSize
).minmax
!"max"(0.0f).length
)};
579 static immutable cylParams
= Vec3(0.0f, 0.0f, 0.6f); // x, y, width
580 enum objfuncCyl
= q
{(Vec3
.length(p
.x
-cylParams
.x
, p
.z
-cylParams
.y
)-cylParams
.z
)};
590 float mapWorld() (in auto ref Vec3 p
, ref int obj
) {
591 static if (__VERSION__
> 2068) pragma(inline
, true); // yep, 2.068
592 // we need object number to distinguish floor from other objects
593 // we can paint objects too by getting their numbers ;-)
594 // subtraction always gives us roundbox
595 obj
= ObjId
.roundbox
;
597 //float res = objopSub(mixin(objfuncRoundbox), mixin(objfuncSphere));
598 //float res = objopSub(mixin(objfuncBox), mixin(objfuncSphere));
599 //float res = objopSub(mixin(objfuncBoxu), mixin(objfuncSphere));
600 float res
= objopSub(mixin(objfuncCyl
), mixin(objfuncSphere
));
601 obj
= objopUnion(res
, mixin(objfuncTorus
), res
, ObjId
.torus
, obj
);
602 obj
= objopUnion(res
, mixin(objfuncFloor
), res
, ObjId
.floor
, obj
);
607 // ////////////////////////////////////////////////////////////////////////// //
608 // floor color (checkerboard)
609 Vec3
floorColor() (in auto ref Vec3 p
) {
610 static if (__VERSION__
> 2067) pragma(inline
, true);
612 if (fract(p
.x
*0.2f) > 0.2f) {
613 if (fract(p
.z
*0.2f) > 0.2f) {
618 res
.x
= res
.y
= res
.z
= 1.0f;
621 if (fract(p
.z
*0.2f) > 0.2f) {
622 res
.x
= res
.y
= res
.z
= 1.0f;
633 // ////////////////////////////////////////////////////////////////////////// //
636 void raymarch() (float x
, float y
, in auto ref Vec3 scp
, in auto ref Vec3 L
, ref Vec3 color
) {
637 enum maxd
= 100.0f; // max depth
638 Vec3 c
= void, p
= void, N
= void;
643 foreach (immutable _
; 0..256) {
644 import std
.math
: abs
;
645 if (abs(dx
) < 0.001 || f
> maxd
) break;
648 dx
= mapWorld(p
, obj
);
651 // did we hit something?
653 // get primitive color
659 c
= Vec3(0.0f, 0.6f, 0.0f);
662 c
= Vec3(0.6f, 0.6f, 0.8f);
671 import std
.math
: pow
;
672 N
= Vec3(dx
-mapWorld(p
-exyy
, obj
),
673 dx
-mapWorld(p
-eyxy
, obj
),
674 dx
-mapWorld(p
-eyyx
, obj
)).normalize
;
675 float b
= N
.dot((prp
-p
+L
).normalize
);
676 // simple phong lighting, LightPosition = CameraPosition
677 color
= (c
*b
+pow(b
, 16.0f))*(1.0f-f
*0.01f);
679 color
.x
= color
.y
= color
.z
= 0.0f; // background color
684 // ////////////////////////////////////////////////////////////////////////// //
685 void renderToTexture () {
686 import std
.math
: sin
, cos
;
688 // vertex shader common part
689 auto vpn
= (vrp
-prp
).normalize
;
690 auto u
= (vuv
%vpn
).normalize
;
694 //vertex shader for each vertex
695 immutable float[2][4] vPos
= [
703 Vec3 scrCoord
= void; // temp
704 immutable float cxy
= cast(float)vlWidth
/cast(float)vlHeight
;
705 foreach (immutable i
; 0..vPos
.length
) {
706 scrCoord
.x
= vcv
.x
+vPos
.ptr
[i
].ptr
[0]*u
.x
*cxy
+vPos
.ptr
[i
].ptr
[1]*v
.x
;
707 scrCoord
.y
= vcv
.y
+vPos
.ptr
[i
].ptr
[0]*u
.y
*cxy
+vPos
.ptr
[i
].ptr
[1]*v
.y
;
708 scrCoord
.z
= vcv
.z
+vPos
.ptr
[i
].ptr
[0]*u
.z
*cxy
+vPos
.ptr
[i
].ptr
[1]*v
.z
;
709 scp
.ptr
[i
] = (scrCoord
-prp
).normalize
;
712 float x
= -0.5f, y
= -0.5f, x_inc
= 1.0f/vlWidth
, y_inc
= 1.0f/vlHeight
;
714 auto grady1v
= (scp
.ptr
[3]-scp
.ptr
[0])*y_inc
;
715 auto accy1v
= scp
.ptr
[0];
717 auto grady2v
= (scp
.ptr
[2]-scp
.ptr
[1])*y_inc
;
718 auto accy2v
= scp
.ptr
[1];
720 Vec3 colorout
= void;
721 Vec3 gradxv
= void, accxv
= void;
722 Vec3 L
= Vec3(sin(worldtime
)*20.0f, 10.0f, cos(worldtime
)*20.0f);
724 auto nextLineAdjustment
= texImage
.adjustmentForNextLine();
725 auto offR
= texImage
.redByteOffset();
726 auto offB
= texImage
.blueByteOffset();
727 auto offG
= texImage
.greenByteOffset();
728 auto bpp
= texImage
.bytesPerPixel();
729 auto imgdata
= texImage
.getDataPointer();
730 // figure out the starting byte offset
731 auto offset
= texImage
.offsetForTopLeftPixel();//+nextLineAdjustment*Y+bpp*X;
732 auto startOfLine
= imgdata
+offset
; // get our pointer lined up on the first pixel
734 foreach (int iy
; 0..vlHeight
) {
735 auto vbptr
= startOfLine
; // we keep the start of line separately so moving to the next line is simple and portable
736 startOfLine
+= nextLineAdjustment
*scale
;
738 gradxv
= (accy2v
-accy1v
)*x_inc
;
741 foreach (int ix
; 0..vlWidth
) {
742 raymarch(x
, y
, accxv
, L
, colorout
);
743 static if (scale
> 1) {
744 ubyte r
= clampToByte(cast(int)(colorout
.x
*255.0f));
745 ubyte g
= clampToByte(cast(int)(colorout
.y
*255.0f));
746 ubyte b
= clampToByte(cast(int)(colorout
.z
*255.0f));
747 foreach (immutable _
; 0..scale
) {
751 static if (!scanlines
) {
753 foreach (immutable _1
; 1..scale
) {
754 vbptrv
+= nextLineAdjustment
;
763 vbptr
[offR
] = clampToByte(cast(int)(colorout
.x
*255.0f));
764 vbptr
[offG
] = clampToByte(cast(int)(colorout
.y
*255.0f));
765 vbptr
[offB
] = clampToByte(cast(int)(colorout
.z
*255.0f));
781 __gshared MonoTime sttime
;
784 __gshared
bool firstTime
= true;
786 sttime
= MonoTime
.currTime
;
790 import std
.math
: sin
, cos
;
792 worldtime
= cast(float)(MonoTime
.currTime
-sttime
).total
!"msecs"/1000.0f;
794 Vec3 auto_vuv
= void, auto_vrp
= void, auto_prp
= void;
797 auto_vuv
.x
= sin(worldtime
/*timemsecs*/);
801 // view reference point
802 auto_vrp
.x
= 0; //sin(time*0.7f)*10.0f;
804 auto_vrp
.z
= 0; //cos(time*0.9f)*10.0f;
807 auto_prp
.x
= 3.0f; //sin(time*0.7f)*20.0f+auto_vrp.x+20.0f;
808 auto_prp
.y
= 3.0f; //sin(time)*4.0f+4.0f+auto_vrp.y+3.0f;
809 auto_prp
.z
= 3.0f; //cos(time*0.6f)*20.0f+auto_vrp.z+14.0f;
818 if (sdwindow
.closed
) return;
819 auto painter
= sdwindow
.draw();
820 painter
.drawImage(Point(0, 0), texImage
);
824 void main (string
[] args
) {
825 texImage
= new Image(vlEffectiveWidth
, vlEffectiveHeight
);
830 sdwindow
= new SimpleWindow(vlEffectiveWidth
, vlEffectiveHeight
, "RayMarching demo", OpenGlOptions
.no
, Resizablity
.fixedSize
);
833 enum MSecsPerFrame
= 1000/25; /* 25 is FPS */
834 int framesToSkip
= 0;
836 sdwindow
.eventLoop(MSecsPerFrame
,
838 if (sdwindow
.closed
) return;
839 if (--framesToSkip
>= 0) return;
840 auto cft
= MonoTime
.currTime
;
843 auto frameDur
= cast(int)(MonoTime
.currTime
-cft
).total
!"msecs";
844 if (frameDur
>= MSecsPerFrame
) {
845 import core
.stdc
.stdio
;
846 //printf("TUUUURTLE! MSecsPerFrame=%u; msecs=%u\n", cast(uint)MSecsPerFrame, cast(uint)frameDur);
847 //framesToSkip = (frameDur-1)/MSecsPerFrame;
848 framesToSkip
= frameDur
/MSecsPerFrame
+1;
855 delegate (KeyEvent event
) {
856 if (sdwindow
.closed
) return;
857 if (event
.key
== Key
.Escape
) {
862 delegate (MouseEvent event
) {
864 delegate (dchar ch
) {
867 if (!sdwindow
.closed
) {