initial commit
[raymarch.git] / zrm3_adam_full_prims.d
blobf02b99d719a698fdf0b28cd59f601d7ae2f84ac7
1 /*
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;
24 } else {
25 public import simpledisplay;
28 __gshared SimpleWindow sdwindow;
29 __gshared Image texImage;
31 shared static ~this () {
32 destroy(texImage);
35 enum vlWidth = 160;
36 enum vlHeight = 120;
37 enum scale = 3;
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)));
51 } else {
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?");
57 return cast(ubyte)n;
58 } else {
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,];
154 void drawDisk () {
155 auto diskp = iconDisk.ptr;
156 auto w = *diskp++;
157 auto h = *diskp++;
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;
166 auto Y = 0;
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) {
173 ubyte r = *diskp++;
174 ubyte g = *diskp++;
175 ubyte b = *diskp++;
176 foreach (immutable _; 0..scale) {
177 vbptr[offR] = r;
178 vbptr[offG] = g;
179 vbptr[offB] = b;
180 static if (!scanlines) {
181 auto vbptrv = vbptr;
182 foreach (immutable _2; 1..scale) {
183 vbptrv += nextLineAdjustment;
184 vbptrv[offR] = r;
185 vbptrv[offG] = g;
186 vbptrv[offB] = b;
189 vbptr += bpp;
196 // ////////////////////////////////////////////////////////////////////////// //
197 struct Vec3 {
198 import std.format : FormatSpec;
199 void toString (scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const @trusted {
200 import std.format;
201 sink("Vec3(");
202 sink.formatValue(x, fmt);
203 sink(",");
204 sink.formatValue(y, fmt);
205 sink(",");
206 sink.formatValue(z, fmt);
207 sink(")");
210 nothrow @safe @nogc:
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");
222 final switch (idx) {
223 case 0: x = v; break;
224 case 1: y = v; break;
225 case 2: z = v; break;
227 return v;
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);
234 x *= invlen;
235 y *= invlen;
236 z *= invlen;
237 return this;
240 const pure {
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);
253 float length () {
254 //pragma(inline, true);
255 import std.math : sqrt;
256 return sqrt(x*x+y*y+z*z);
257 //return length(x, y, z);
260 Vec3 normalized () {
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);
267 Vec3 abs () {
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 {
333 fld angle;
334 fsincos;
335 fstp [cosine];
336 fstp [sine];
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 {
345 fld angle;
346 fsincos;
347 fstp [cosine];
348 fstp [sine];
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 {
357 fld angle;
358 fsincos;
359 fstp [cosine];
360 fstp [sine];
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;");
372 return this;
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;
396 return x-floor(x);
400 // is this correct?
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
411 // repetition
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);
443 // intersection
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);
452 if (obj0 < obj1) {
453 dest = obj0;
454 return oid0;
455 } else {
456 dest = obj1;
457 return oid1;
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);
464 b = -b;
465 if (a > b) {
466 dest = a;
467 return oid0;
468 } else {
469 dest = b;
470 return oid1;
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);
477 if (obj0 > obj1) {
478 dest = obj0;
479 return oid0;
480 } else {
481 dest = obj1;
482 return oid1;
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);
495 return d1+d2;
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);
504 return -log(res)/k;
507 // power smooth min (k = 8);
508 float sminPow (float a, float b, float k) {
509 import std.math : pow;
510 a = pow(a, k);
511 b = pow(b, k);
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);
553 enum planeD = 10.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)};
560 // sphere, signed
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)};
574 // box, unsigned
575 static immutable boxuSize = Vec3(2.0f, 0.7f, 2.0f);
576 enum objfuncBoxu = q{((p.abs-boxuSize).minmax!"max"(0.0f).length)};
578 // cylinder, signed
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)};
582 // object ids:
583 enum ObjId : int {
584 floor = 0,
585 roundbox = 1,
586 torus = 2,
587 box = 3,
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;
596 //obj = ObjId.floor;
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);
603 return res;
607 // ////////////////////////////////////////////////////////////////////////// //
608 // floor color (checkerboard)
609 Vec3 floorColor() (in auto ref Vec3 p) {
610 static if (__VERSION__ > 2067) pragma(inline, true);
611 Vec3 res = void;
612 if (fract(p.x*0.2f) > 0.2f) {
613 if (fract(p.z*0.2f) > 0.2f) {
614 res.x = 0.0f;
615 res.y = 0.1f;
616 res.z = 0.2f;
617 } else {
618 res.x = res.y = res.z = 1.0f;
620 } else {
621 if (fract(p.z*0.2f) > 0.2f) {
622 res.x = res.y = res.z = 1.0f;
623 } else {
624 res.x = 0.3f;
625 res.y = 0.0f;
626 res.z = 0.0f;
629 return res;
633 // ////////////////////////////////////////////////////////////////////////// //
634 // raymarching
635 // L: light
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;
639 float dx = 0.1f;
640 int obj = 0;
642 float f = 1.0f;
643 foreach (immutable _; 0..256) {
644 import std.math : abs;
645 if (abs(dx) < 0.001 || f > maxd) break;
646 f += dx;
647 p = prp+scp*f;
648 dx = mapWorld(p, obj);
651 // did we hit something?
652 if (f < maxd) {
653 // get primitive color
654 switch (obj) {
655 case ObjId.floor:
656 c = floorColor(p);
657 break;
658 case ObjId.roundbox:
659 c = Vec3(0.0f, 0.6f, 0.0f);
660 break;
661 case ObjId.torus:
662 c = Vec3(0.6f, 0.6f, 0.8f);
663 break;
664 default:
665 // RED
666 c.x = 1.0f;
667 c.y = c.z = 0;
668 break;
670 // do lighting
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);
678 } else {
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;
691 auto v = vpn%u;
692 auto vcv = prp+vpn;
694 //vertex shader for each vertex
695 immutable float[2][4] vPos = [
696 [-1, 1], //0--1
697 [ 1, 1], //| |
698 [ 1, -1], //3--2
699 [-1, -1],
702 Vec3[4] scp = void;
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;
739 accxv = accy1v;
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) {
748 vbptr[offR] = r;
749 vbptr[offG] = g;
750 vbptr[offB] = b;
751 static if (!scanlines) {
752 auto vbptrv = vbptr;
753 foreach (immutable _1; 1..scale) {
754 vbptrv += nextLineAdjustment;
755 vbptrv[offR] = r;
756 vbptrv[offG] = g;
757 vbptrv[offB] = b;
760 vbptr += bpp;
762 } else {
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));
766 vbptr += bpp;
768 x += x_inc;
769 accxv += gradxv;
772 accy1v += grady1v;
773 accy2v += grady2v;
775 y += y_inc;
776 x = -0.5f;
781 __gshared MonoTime sttime;
783 void animate () {
784 __gshared bool firstTime = true;
785 if (firstTime) {
786 sttime = MonoTime.currTime;
787 firstTime = false;
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;
796 // view up vector
797 auto_vuv.x = sin(worldtime/*timemsecs*/);
798 auto_vuv.y = 1;
799 auto_vuv.z = 0;
801 // view reference point
802 auto_vrp.x = 0; //sin(time*0.7f)*10.0f;
803 auto_vrp.y = 0;
804 auto_vrp.z = 0; //cos(time*0.9f)*10.0f;
806 // camera position
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;
811 vuv = auto_vuv;
812 vrp = auto_vrp;
813 prp = auto_prp;
817 void blitScene () {
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);
827 animate();
828 renderToTexture();
830 sdwindow = new SimpleWindow(vlEffectiveWidth, vlEffectiveHeight, "RayMarching demo", OpenGlOptions.no, Resizablity.fixedSize);
831 blitScene();
833 enum MSecsPerFrame = 1000/25; /* 25 is FPS */
834 int framesToSkip = 0;
836 sdwindow.eventLoop(MSecsPerFrame,
837 delegate () {
838 if (sdwindow.closed) return;
839 if (--framesToSkip >= 0) return;
840 auto cft = MonoTime.currTime;
841 animate();
842 renderToTexture();
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;
849 drawDisk();
850 } else {
851 framesToSkip = 0;
853 blitScene();
855 delegate (KeyEvent event) {
856 if (sdwindow.closed) return;
857 if (event.key == Key.Escape) {
858 flushGui();
859 sdwindow.close();
862 delegate (MouseEvent event) {
864 delegate (dchar ch) {
867 if (!sdwindow.closed) {
868 flushGui();
869 sdwindow.close();
871 flushGui();