cosmetix
[stoyd.git] / xmain.d
blobb934d73f7d3836f0870e8a78e03bab4f1b5cd4ee
1 // main driver
2 module xmain_mt/* is aliced*/;
4 private:
5 import core.atomic;
6 import core.thread;
7 import core.time;
9 import glbinds;
10 import glutils;
13 // ////////////////////////////////////////////////////////////////////////// //
14 //version = use_vsync;
17 // ////////////////////////////////////////////////////////////////////////// //
18 enum WindowTitle = "ShaderToy emulator";
21 // ////////////////////////////////////////////////////////////////////////// //
22 //#extension GL_ARB_compatibility : enable
23 enum ShaderPre = q{
24 #version 120
25 uniform vec3 iResolution; // viewport resolution (in pixels)
26 uniform vec4 iMouse; // mouse pixel coords
27 uniform float iGlobalTime; // shader playback time (in seconds)
28 uniform float iGlobalFrame; // ???
29 uniform float iTimeDelta; // how long last frame took to render, in seconds (TODO)
30 uniform float iFrame; // buffer shaders seems to have this
31 uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
32 uniform float iChannelTime[4]; // channel playback time (in sec)
33 uniform vec4 iDate; // (year, month, day, time in seconds)
34 //uniform float iGlobalDelta; // i don't know
35 //uniform float iSampleRate; // sound sample rate (i.e., 44100)
38 enum ShaderMain = q{
39 void main (void) {
40 vec2 fc = vec2(gl_FragCoord.x, gl_FragCoord.y);
41 mainImage(gl_FragColor, fc);
42 gl_FragColor.w = 1.0;
46 // ////////////////////////////////////////////////////////////////////////// //
47 import arsd.color;
48 import arsd.png;
51 __gshared SimpleWindow sdwindow;
54 public enum vlWidth = 800;
55 public enum vlHeight = 500;
56 public enum scale = 1;
58 public enum vlEffectiveWidth = vlWidth*scale;
59 public enum vlEffectiveHeight = vlHeight*scale;
62 // ////////////////////////////////////////////////////////////////////////// //
63 __gshared string basePath;
64 __gshared string shaderFile;
65 __gshared string shaderSource;
66 __gshared Texture[4] textures; // 4 texture samplers
67 __gshared TextureCube[4] texturesCube; // 4 cube texture samplers (can be null)
68 __gshared Texture texMain;
69 __gshared Shader shader;
70 __gshared Shader[4] shaderbufs; // a,b,c,d
71 __gshared int[4] bufmap = -1; // bufmap[texnum] == bufN (or -1)
72 __gshared FBO[4] buffbos;
73 __gshared GLuint listQuad;
76 // ////////////////////////////////////////////////////////////////////////// //
77 void initOpenGL () {
78 static auto loadShader (string sname, string src) {
79 string spre = ShaderPre~"\n";
80 // setup texture samplers
81 foreach (int idx; 0..4) {
82 import std.string : format;
83 if (texturesCube[idx] !is null) {
84 spre ~= "uniform samplerCube iChannel%s;\n".format(idx);
85 } else {
86 spre ~= "uniform sampler2D iChannel%s;\n".format(idx);
89 spre ~= "#line 0\n";
90 auto shader = new Shader(sname, spre~src~"\n"~ShaderMain);
91 shader.exec({
92 shader["iResolution"] = SVec3F(vlWidth, vlHeight, 0.0f);
93 foreach (int idx; 0..4) {
94 char[9] vname = "iChannel0";
95 vname[$-1] = cast(char)('0'+idx);
96 int bufidx = bufmap[idx];
97 if (bufidx < 0) bufidx = idx;
98 shader[vname[]] = cast(int)bufidx;
100 auto cri = shader.varId("iChannelResolution");
101 float[3][4] cres = void;
102 foreach (int idx; 0..4) {
103 if (texturesCube[idx]) {
104 cres[idx][0] = texturesCube[idx].width;
105 cres[idx][1] = texturesCube[idx].height;
106 } else {
107 int bufidx = bufmap[idx];
108 if (bufidx < 0) bufidx = idx;
109 cres[idx][0] = textures[bufidx].width;
110 cres[idx][1] = textures[bufidx].height;
112 cres[idx][2] = 0;
114 glUniform3fv(cri, 4, &cres[0][0]);
116 return shader;
119 glEnable(GL_TEXTURE_2D);
120 glDisable(GL_LIGHTING);
121 glDisable(GL_DITHER);
122 glDisable(GL_BLEND);
123 glDisable(GL_DEPTH_TEST);
125 // load textures
126 try {
127 import std.path;
128 import std.stdio : File;
129 int num = 0;
130 foreach (/*immutable*/ line; File(shaderFile.setExtension(".tex")).byLine) {
131 Texture tex;
132 bufmap[num] = -1;
133 if (line == "bufa" || line == "bufb" || line == "bufc" || line == "bufd") {
134 // render buffer
135 int bufidx = line[3]-'a';
136 bufmap[num] = bufidx;
137 tex = new Texture(vlWidth, vlHeight, true, Texture.Option.fp, Texture.Option.nearest, Texture.Option.clamp); // it's floating point, i guess
138 } else if (line.length > 4 && line[0..4] == "cube") {
139 // load cubemap texture
140 import std.string : format;
141 try {
142 string fn = basePath~"/textures/cube/%s_%%s.png".format(line);
143 auto texc = new TextureCube(fn);
144 texturesCube[num++] = texc;
145 if (num >= textures.length) break;
146 continue;
147 } catch (Exception e) {
148 import std.stdio;
149 writeln("can't load cube texture '", line, "'");
150 assert(0);
151 //tex = null;
153 } else {
154 import std.string : format;
155 try {
156 string fn = basePath~"/textures/tex%s.png".format(line);
157 tex = new Texture(fn);
158 } catch (Exception e) {
159 import std.stdio;
160 writeln("can't load texture '", line, "'");
161 tex = null;
164 textures[num++] = tex;
165 if (num >= textures.length) break;
167 } catch (Exception e) {}
168 //foreach (immutable num; 0..4) if (textures[num] is null) textures[num] = new Texture(512, 512);
169 foreach (immutable num; 0..4) {
170 if (textures[num] is null && texturesCube[num] is null) {
171 //textures[num] = new Texture(basePath~"/textures/tex00.png");
172 textures[num] = new Texture(vlWidth, vlHeight, true/*, Texture.Option.fp*/);
175 texMain = new Texture(vlWidth, vlHeight, true);
177 foreach (int idx; 0..4) {
178 glActiveTexture(GL_TEXTURE0+idx);
179 if (texturesCube[idx] !is null) {
180 glBindTexture(GL_TEXTURE_CUBE_MAP, texturesCube[idx].tid);
181 } else {
182 int bufidx = bufmap[idx];
183 if (bufidx < 0) bufidx = idx;
184 glBindTexture(GL_TEXTURE_2D, textures[bufidx].tid);
187 glActiveTexture(GL_TEXTURE4);
188 glBindTexture(GL_TEXTURE_2D, texMain.tid);
190 // create shader
191 shader = loadShader("shader", shaderSource);
193 // load buffer shaders, if necessary
194 foreach (int idx; 0..4) {
195 int bufidx = bufmap[idx];
196 if (bufidx < 0) continue;
197 if (shaderbufs[bufidx] is null) {
198 // load shader
199 import std.file : readText;
200 import std.string : format;
201 import std.path : dirName, setExtension;
202 string fname = shaderFile.setExtension("")~"_buf%c.frag".format(cast(char)('a'+bufidx));
203 { import std.stdio; writeln("reading buffer shader: '", fname, "'"); }
204 shaderbufs[bufidx] = loadShader("buf%c".format(cast(char)('a'+bufidx)), readText(fname));
205 // create fbo
206 buffbos[bufidx] = new FBO(textures[bufidx]);
210 // setup matrices
212 glMatrixMode(GL_PROJECTION);
213 glLoadIdentity();
214 glOrtho(0, vlWidth, vlHeight, 0, -1, 1);
216 orthoCamera(vlWidth, vlHeight);
218 glMatrixMode(GL_MODELVIEW);
219 glLoadIdentity();
221 // create display list for quad
223 enum x0 = 0;
224 enum y0 = 0;
225 enum x1 = vlWidth;
226 enum y1 = vlHeight;
227 listQuad = glGenLists(1);
228 glNewList(listQuad, GL_COMPILE);
229 glBegin(GL_QUADS);
230 glTexCoord2f(0.0f, 0.0f); glVertex2i(x0, y0); // top-left
231 glTexCoord2f(1.0f, 0.0f); glVertex2i(x1, y0); // top-right
232 glTexCoord2f(1.0f, 1.0f); glVertex2i(x1, y1); // bottom-right
233 glTexCoord2f(0.0f, 1.0f); glVertex2i(x0, y1); // bottom-left
234 glEnd();
235 glEndList();
237 //glDeleteLists(index, 1);
241 // ////////////////////////////////////////////////////////////////////////// //
242 __gshared int mouseX = 0, mouseY = vlHeight-1;
243 __gshared bool mBut0 = false, mBut1 = false;
244 __gshared float globalTime = 0.0f, frameTime = 0.0f;
245 __gshared float globalFrame = 0.0f;
246 __gshared bool paused = false;
247 shared int diedie = 0;
250 // ////////////////////////////////////////////////////////////////////////// //
251 void setShaderArgs (Shader shader) {
252 //shader["iMouse"] = SVec4F(mouseX, vlHeight-1-mouseY, cast(float)mouseX/cast(float)(vlWidth-1), cast(float)(vlHeight-1-mouseY)/cast(float)(vlHeight-1));
253 if (mBut0) {
254 shader["iMouse"] = SVec4F(mouseX, vlHeight-1-mouseY, mouseX, vlHeight-1-mouseY);
255 } else {
256 shader["iMouse"] = SVec4F(mouseX, vlHeight-1-mouseY, 0.0f, 0.0f);
258 //prevmouseX = mouseX;
259 //prevmouseY = (vlHeight-1-mouseY);
260 shader["iGlobalTime"] = globalTime;
261 shader["iChannelTime"] = SVec4F(globalTime, globalTime, globalTime, globalTime);
262 shader["iGlobalFrame"] = globalFrame;
263 shader["iFrame"] = globalFrame;
264 shader["iTimeDelta"] = frameTime;
265 auto vid = shader.varId("iDate");
266 if (vid >= 0) {
267 import std.datetime;
268 auto now = Clock.currTime;
269 glUniform4f(vid,
270 now.year,
271 now.month,
272 now.day,
273 cast(float)now.hour*3600.0f+cast(float)now.minute*60.0f+cast(float)now.second+cast(float)now.fracSecs.total!"msecs"/1000.0f
279 void renderFrame (bool paused) {
280 // first update buffers, if any
281 if (!paused) {
282 foreach (int idx; 0..4) {
283 if (auto sd = shaderbufs[idx]) {
284 buffbos[idx].exec({
285 sd.exec({
286 setShaderArgs(sd);
287 glCallList(listQuad);
293 shader.exec({
294 setShaderArgs(shader);
295 glCallList(listQuad);
300 // ////////////////////////////////////////////////////////////////////////// //
301 void renderThread () {
302 bool oldpaused = paused;
303 MonoTime prevFrameStartTime = MonoTime.currTime;
304 version(use_vsync) {} else MonoTime ltt = MonoTime.currTime;
305 MonoTime lasttime = MonoTime.currTime;
306 MonoTime time;
307 enum MaxFPSFrames = 16;
308 float frtimes = 0.0f;
309 int framenum = 0;
310 int prevFPS = -1;
311 bool first = true;
312 for (;;) {
313 if (sdwindow.closed) break;
314 if (atomicLoad(diedie) > 0) break;
316 time = MonoTime.currTime;
317 version(use_vsync) {
318 } else {
319 // max 60 FPS; capped by vsync
320 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
321 if (!first && (time-ltt).total!"msecs" < 16) {
322 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
323 import core.sys.posix.signal : timespec;
324 import core.sys.posix.time : nanosleep;
325 timespec ts = void;
326 ts.tv_sec = 0;
327 ts.tv_nsec = (16-cast(int)((time-ltt).total!"msecs"))*1000*1000; // milli to nano
328 nanosleep(&ts, null); // idc how much time was passed
329 time = MonoTime.currTime;
331 ltt = time;
332 first = false;
335 auto pau = paused;
336 if (oldpaused != pau) {
337 lasttime = time;
338 prevFrameStartTime = time;
339 oldpaused = pau;
341 if (!pau) {
342 globalTime += cast(float)(time-lasttime).total!"msecs"/1000.0f;
343 lasttime = time;
344 globalFrame += 1.0;
345 debug { import core.stdc.stdio; printf("globalTime=%f\n", globalTime); }
347 frameTime = cast(float)(time-prevFrameStartTime).total!"msecs"/1000.0f;
348 prevFrameStartTime = time;
350 //{ import core.stdc.stdio; printf("frametime: %f\n", frameTime*1000.0f); }
351 //{ import core.stdc.stdio; printf("FPS: %d\n", cast(int)(1.0f/frameTime)); }
352 frtimes += frameTime;
353 if (++framenum >= MaxFPSFrames || frtimes >= 3.0f) {
354 import std.string : format;
355 int newFPS = cast(int)(cast(float)MaxFPSFrames/frtimes+0.5);
356 if (newFPS != prevFPS) {
357 sdwindow.title = "%s / FPS:%s".format(WindowTitle, newFPS);
358 prevFPS = newFPS;
360 framenum = 0;
361 frtimes = 0.0f;
364 //debug { import core.stdc.stdio; printf("XLockDisplay()\n"); }
365 //XLockDisplay(sdwindow.display);
366 debug { import core.stdc.stdio; printf("glXMakeCurrent()\n"); }
367 if (glXMakeCurrent(sdwindow.display, sdwindow.window, sdwindow.glc) == 0) {
368 { import core.stdc.stdio; printf(" FUUUU\n"); }
370 debug { import core.stdc.stdio; printf("renderFrame()\n"); }
371 renderFrame(pau);
372 debug { import core.stdc.stdio; printf("glXSwapBuffers()\n"); }
373 glXSwapBuffers(sdwindow.display, sdwindow.window);
374 debug { import core.stdc.stdio; printf("glFinish()\n"); }
375 glFinish();
376 // release context
377 debug { import core.stdc.stdio; printf("glXMakeCurrent(0)\n"); }
378 glXMakeCurrent(sdwindow.display, 0, null);
379 //debug { import core.stdc.stdio; printf("XUnlockDisplay()\n"); }
380 //XUnlockDisplay(sdwindow.display);
383 atomicStore(diedie, 2);
387 // ////////////////////////////////////////////////////////////////////////// //
388 void closeWindow () {
389 if (atomicLoad(diedie) != 2) {
390 atomicStore(diedie, 1);
391 while (atomicLoad(diedie) != 2) {}
393 if (!sdwindow.closed) {
394 flushGui();
395 sdwindow.close();
400 // ////////////////////////////////////////////////////////////////////////// //
401 void main (string[] args) {
402 if (args.length < 2) assert(0, "filename?");
403 if (XInitThreads() == 0) assert(0, "XMT fucked");
406 import std.algorithm : startsWith, endsWith;
407 import std.file : readText, thisExePath;
408 import std.path : dirName;
409 version(rdmd) {
410 basePath = "/home/ketmar/DUMMY-FUCK-MC/glsl_raymarching/d_glsl";
411 } else {
412 basePath = thisExePath.dirName;
414 shaderFile = args[1];
415 if (shaderFile.endsWith(".frag")) shaderFile = shaderFile[0..$-5];
416 if (shaderFile.length && shaderFile[0] != '.') {
417 if (shaderFile.startsWith("shaders/")) shaderFile = shaderFile[8..$];
418 shaderFile = basePath~"/shaders/"~shaderFile~".frag";
420 shaderFile = shaderFile~".frag";
421 shaderSource = readText(shaderFile);
424 Thread renderTid;
426 sdwindow = new SimpleWindow(vlEffectiveWidth, vlEffectiveHeight, WindowTitle, OpenGlOptions.yes, Resizablity.fixedSize);
428 sdwindow.visibleForTheFirstTime = delegate () {
429 sdwindow.setAsCurrentOpenGlContext(); // make this window active
430 version(use_vsync) {
431 sdwindow.vsync = true;
432 } else {
433 sdwindow.vsync = false;
435 //sdwindow.useGLFinish = false;
436 initOpenGL();
438 //sdwindow.redrawOpenGlScene();
439 renderFrame(paused);
440 sdwindow.swapOpenGlBuffers();
441 glFinish();
442 glFlush();
443 // release context
444 if (glXMakeCurrent(sdwindow.display, 0, null) == 0) { import core.stdc.stdio; printf("can't release OpenGL context(0)\n"); }
445 glFinish();
446 glFlush();
450 sdwindow.eventLoop(100,
451 delegate () {
452 if (sdwindow.closed) return;
453 if (!renderTid) {
454 if (glXMakeCurrent(sdwindow.display, 0, null) == 0) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); }
455 renderTid = new Thread(&renderThread);
456 renderTid.start();
458 //sdwindow.redrawOpenGlSceneNow();
460 delegate (KeyEvent event) {
461 if (sdwindow.closed) return;
462 if (event.pressed && event.key == Key.Escape) closeWindow();
464 delegate (MouseEvent event) {
465 if (sdwindow.closed) return;
466 mouseX = event.x/scale;
467 mouseY = event.y/scale;
468 if (event.type == MouseEventType.buttonPressed) {
469 if (event.button == MouseButton.left) mBut0 = true;
470 if (event.button == MouseButton.right) mBut1 = true;
471 } else if (event.type == MouseEventType.buttonReleased) {
472 if (event.button == MouseButton.left) mBut0 = false;
473 if (event.button == MouseButton.right) mBut1 = false;
476 delegate (dchar ch) {
477 if (ch == 'q') closeWindow();
478 if (ch == '0') { mouseX = mouseY = 0; }
479 if (ch == ' ') { paused = !paused; }
482 closeWindow();
483 flushGui();