; ***********************************************************
; *                       [ Lesser ]                        *
; ***********************************************************
; *   Framework, Visuals, Design, Synth, Music:    Ferris   *
; ***********************************************************
; * Thanks  to  Psycho / Loonies  for  his  (however brief) *
; * explanation of the sphere tracing algorithm,  which has *
; * been  beaten,   battered,   and  pwned  throughout  the *
; * development of this intro.                              *
; ***********************************************************
; *                [c] 2009 Youth Uprising                  *
; ***********************************************************

%define _4K_SCRW_ 1280
%define _4K_SCRH_ 720

;%define _4K_FAST_FORWARD_ 116300
%define _4K_END_TIME_ 136000

;%define _4K_SAFE_EXE_ ; Uncomment to use safe .exe specs (proper window setup etc) <- Make sure to include default libs (DOESN'T WORK YET)
;%define _4K_DIAGNOSTICS_ ; Uncomment to enable message boxes at each major initialization step
%define _4K_RESIZE_WINDOW_ ; Uncomment to resize window to _4K_SCRW_ x _4K_SCRH_
;%define _4K_NO_SHADERS_ ; Uncomment to disable shaders
%define _4K_NO_VERTEX_SHADER_ ; Uncomment to disable vertex shader only
;%define _4K_NO_SOUND_ ; Uncomment to disable sound

%include "..\Include\win32n.inc"
%include "..\Include\nasmgl.inc"

; Windows
%define ChangeDisplaySettingsA _ChangeDisplaySettingsA@8
extern ChangeDisplaySettingsA
%define ShowCursor _ShowCursor@4
extern ShowCursor
%define CreateWindowExA _CreateWindowExA@48
extern CreateWindowExA
%define GetDC _GetDC@4
extern GetDC
%define ChoosePixelFormat _ChoosePixelFormat@8
extern ChoosePixelFormat
%define SetPixelFormat _SetPixelFormat@12
extern SetPixelFormat
%define wglCreateContext _wglCreateContext@4
extern wglCreateContext
%define wglMakeCurrent _wglMakeCurrent@8
extern wglMakeCurrent
%define timeGetTime _timeGetTime@0
extern timeGetTime
%define SwapBuffers _SwapBuffers@4
extern SwapBuffers
%define GetAsyncKeyState _GetAsyncKeyState@4
extern GetAsyncKeyState
%define ExitProcess _ExitProcess@4
extern ExitProcess
%ifdef _4K_DIAGNOSTICS_
%define MessageBoxA _MessageBoxA@16
extern MessageBoxA
%endif

; OpenGL
%define wglGetProcAddress _wglGetProcAddress@4
extern wglGetProcAddress
%define glColor3uiv _glColor3uiv@4
extern glColor3uiv
%define glRects _glRects@16
extern glRects

; Sonant
%ifndef _4K_NO_SOUND_
extern sonant_init
%endif

[segment .text]
					; Entry point
main:
					; Set up stack
					enter 0,0
					
					; Create our window
					;  Hide cursor & possibly change display settings
%ifdef _4K_RESIZE_WINDOW_
					push CDS_FULLSCREEN
					push dmss
					call ChangeDisplaySettingsA
%ifdef _4K_DIAGNOSTICS_
					push MB_OK | MB_ICONEXCLAMATION
					push dg_title
					push dg_resize
					push 0
					call MessageBoxA
%endif
%else
					push byte 0
%endif
					call ShowCursor
					;  Create window
					push byte 0
					push byte 0
					push byte 0
					push byte 0
					push byte 0
					push byte 0
					push byte 0
					push byte 0
					push WS_POPUP | WS_VISIBLE | WS_MAXIMIZE
					push byte 0
					push classname
					push byte 0
					call CreateWindowExA
					;  Get device context
					push eax
					call GetDC
					push pfd
					;  Set pixel format
					mov ebx,eax
					push pfd
					push eax
					call ChoosePixelFormat
					push pfd
					push eax
					push ebx
					call SetPixelFormat
					;  Set up rendering context
					push ebx
					call wglCreateContext
					push eax
					push ebx
					call wglMakeCurrent
%ifdef _4K_DIAGNOSTICS_
					push MB_OK | MB_ICONEXCLAMATION
					push dg_title
					push dg_wcreate
					push 0
					call MessageBoxA
%endif
					
					; Shader init
%ifndef _4K_NO_SHADERS_
					;  Create program
					push sn_glCreateProgram
					call wglGetProcAddress
					call eax
					mov [p],eax
%ifdef _4K_DIAGNOSTICS_
					push MB_OK | MB_ICONEXCLAMATION
					push dg_title
					push dg_spcreate
					push 0
					call MessageBoxA
%endif
%ifndef _4K_NO_VERTEX_SHADER_
					;  Vertex shader
					push sn_glCreateShader
					call wglGetProcAddress
					push 8b31h ; GL_VERTEX_SHADER
					call eax
					mov [pfd],eax
					push sn_glShaderSource
					call wglGetProcAddress
					push byte 0
					;   Have to pass a pointer pointer (not a typo) because glShaderSource only accepts string arrays.
					mov [esi],dword shaders_vertex
					push esi
					push byte 1
					push dword [pfd]
					call eax
					push sn_glCompileShader
					call wglGetProcAddress
					push dword [pfd]
					call eax
					push sn_glAttachShader
					call wglGetProcAddress
					push dword [pfd]
					push dword [p]
					call eax
%ifdef _4K_DIAGNOSTICS_
					push MB_OK | MB_ICONEXCLAMATION
					push dg_title
					push dg_svc
					push 0
					call MessageBoxA
%endif
%endif
					;  Fragment shader
					push sn_glCreateShader
					call wglGetProcAddress
					push 8b30h ; GL_FRAGMENT_SHADER
					call eax
					mov [pfd],eax
					push sn_glShaderSource
					call wglGetProcAddress
					push byte 0
					mov [esi],dword shaders_fragment
					push esi
					push byte 1
					push dword [pfd]
					call eax
					push sn_glCompileShader
					call wglGetProcAddress
					push dword [pfd]
					call eax
					push sn_glAttachShader
					call wglGetProcAddress
					push dword [pfd]
					push dword [p]
					call eax
					;  Link and bind
					push sn_glLinkProgram
					call wglGetProcAddress
					push dword [p]
					call eax
					push sn_glUseProgram
					call wglGetProcAddress
					push dword [p]
					call eax
%ifdef _4K_DIAGNOSTICS_
					push MB_OK | MB_ICONEXCLAMATION
					push dg_title
					push dg_sfc
					push 0
					call MessageBoxA
%endif
%endif
					
					; Music
%ifndef _4K_NO_SOUND_
					call sonant_init
%endif
					
					; Timer init
					call timeGetTime
					mov esi,eax
%ifdef _4K_DIAGNOSTICS_
					push MB_OK | MB_ICONEXCLAMATION
					push dg_title
					push dg_intro
					push 0
					call MessageBoxA
%endif
					
					; Main loop
mainloop:
						;  Timer
						call timeGetTime
						sub eax,esi
						;   Fast forward
%ifdef _4K_FAST_FORWARD_
						add eax,dword _4K_FAST_FORWARD_
%endif
						;   Quit when music finishes
%ifdef _4K_END_TIME_
						cmp eax,dword _4K_END_TIME_
						jge endprogram
%endif
						;   Pass time to shader
						mov [ebp],eax
						push ebp
						call glColor3uiv
						
						;  Render target
						push byte 1
						push byte 1
						push byte -1
						push byte -1
						call glRects
						
						;  Swap buffers
						push ebx
						call SwapBuffers
						
					;  Quit on escape
					push VK_ESCAPE
					call GetAsyncKeyState
					test ax,ax
					jz mainloop
					
					; Terminate
endprogram:			call ExitProcess
					;leave
					;ret

[segment .bss]
p: ; Shader program id
	resd 1

[segment .data]
classname:
	db "edit",0
pfd: ; Pixelformatdescriptor
	dw 0,0
	dd PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER
	db PFD_TYPE_RGBA
	; Risky, but saves 31 uncompressed bytes when removed :)
	;db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	;dd 0,0,0
%ifdef _4K_RESIZE_WINDOW_
dmss: ; Devmode
	db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dw 0,0
	dw DEVMODE_size
	dw 0
	dd DM_PELSWIDTH | DM_PELSHEIGHT
	dw 0,0,0,0,0,0,0,0,0,0,0,0,0
	db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dw 0
	dd 0
	dd _4K_SCRW_
	dd _4K_SCRH_
	; Once again, risky :)
	;dd 0,0,0
%endif
%ifndef _4K_NO_SHADERS_
%ifndef _4K_NO_VERTEX_SHADER_
shaders_vertex: ; Vertex shader
	db "void main(){"
	db  "gl_Position=ftransform();"
	db "}",0
%endif
shaders_fragment: ; Fragment shader
	db "float t,i;"
	db "int o1=0,o2;"
	; Primitives
	db "float f(int o,vec3 p){"
	db  "float s,f;"
	db  "if(o==0)return length(fract((p+1.)*.5)*2.-1.+vec3(0,sin(p.x+sin(p.x+p.y+p.z)*.6+t*.4)*.2,0))-.04;" ; Particles
	db  "if(o==1){" ; Main sphere, growing tube, & sphori
	db   "if(t<56.3){"
	db    "return length(p)-2.;" ; Faster sphere for simplification and optimization
	db   "}else{"
	db    "s=1.-pow(1.-min(max((t-53.3)/20.,0.),1.),2.);"
	db    "f=(sin(p.y+t*.2)*.2+.8)*pow(min(p.y/10.,1.),2.);"
	db    "if(t>=97.5)f=1.;"
	db    "return min(length(p)-2.," ; sphere
	db     "min(max(length(p.xz+vec2(sin(p.y+t),sin(p.y+t*.6))*.6*s*length(p)*.1)-s/max(p.y,1.),length(p-vec3(0,3.*s,0))-3.*s)," ; tube
	db     "max((length(vec3(p.x,fract((p.y-t*.4)*2.)*.5-.25,p.z)+vec3(sin(p.y+t),0,sin(p.y+t*.6))*.6*s*max(length(p),2.)*.1)-min(.1+p.y*.005,.4))*(1.-f)+" ; sph-
	db     "(length(vec2(length(vec2(p.x,p.z))-.4,fract((p.y-t*.4)*2.)*.5-.25))-.15)*f,-p.y)));" ; -ori
	db   "}"
	db  "}"
	db  "if(o==2){" ; Paramecia field
	db   "return length((fract(p*.25)*4.-2.+vec3(0,sin(p.x+t)*sin((p.z+t)*.4)*.3,0))*vec3(.3,1,1))-.2;"
	db  "}"
	db  "if(o==3){" ; Amoeba & tori
	db   "s=pow(max(min((t-116.3)/30.,1.),0.),2.);"
	db   "f=1.+s;"
	db   "return min(length(vec3(p.x*(sin(p.z*2.2+t*f*.8)*.2+.8),p.y*(sin(p.x*2.2+t*f)*.2+.8),p.z*(sin(p.y*2.2+t*f*.9)*.2+.8)))-(2.+s)," ; amoeba
	db    "max(max(length(vec2(length(vec2(p.x,p.z))-(.2+sin(p.y*1.5+t*1.2)*.1),fract((p.y-t*.4)*4.)*.25-.125))-.08,p.y-(t-123.8)*.4),p.y));" ; tori
	db  "}"
	db "}"
	db "void main(){"
	; Time
	db  "t=gl_Color.x*4294967.295;"
	; Eye ray
;	db  "vec3 v=normalize(vec3((gl_FragCoord.xy-vec2(960,540))/1080.,1));" ; Eye ray (1080p)
;	db  "vec3 v=normalize(vec3((gl_FragCoord.xy-vec2(720,450))/900.,1));" ; Eye ray (1440x900)
	db  "vec3 v=normalize(vec3((gl_FragCoord.xy-vec2(640,360))/720.,1));" ; Eye ray (720p)
;	db  "vec3 v=normalize(vec3((gl_FragCoord.xy-vec2(512,384))/768.,1));" ; Eye ray (1024x768)
;	db  "vec3 v=normalize(vec3((gl_FragCoord.xy-vec2(400,300))/600.,1));" ; Eye ray (800x600)
;	db  "vec3 v=normalize(vec3((gl_FragCoord.xy-vec2(320,240))/480.,1));" ; Eye ray (480p)
	; Scenes
	db  "vec3 h3=vec3(1.,.85,.7),e;" ; Default color
	db  "float er,a1,a2,a3;"
	db  "if(t<22.5){" ; Sphere opening
	db   "o2=1;"
	db   "e=vec3(.25,.25,-6.-sin(t*.7)*.4+4.*pow(1.-min(t,5.)/5.,3.));" ; Camera
	db   "if(t>7.51&&t<11.24)e=vec3(e.x-.4,t-7.51-3.,e.z+2.);"
	db   "if(t>=11.24&&t<15.2)e=vec3(t-15.2-2.,e.y-.2,e.z+2.);"
	db   "if(t>18.96)e.z+=3.;"
	db   "er=0;"
	db   "a1=sin(t*.06)*.2;"
	db   "a2=sin(t*.1)*.4;"
	db   "a3=0;"
	db  "}else if(t<56.3){" ; Paramecia field
	db   "h3=vec3(.7,.85,1.);"
	db   "o2=2;"
	db   "e=vec3(cos(t*.1)*-2.,t*.16,-5.5+t*1.6);" ; Camera
	db   "er=0;"
	db   "a1=sin(t*.06)*.3;"
	db   "a2=sin(t*.1)*.5;"
	db   "a3=0;"
	db   "if(t>52.55){"
	db     "a1+=.1;"
	db     "a2=-a2;"
	db     "e-=vec3(0,0,t*1.7);"
	db   "}else if(t<45.){"
	db    "if(t>37.5){"
	db     "a1+=.1;"
	db     "a2=-a2;"
	db     "e-=vec3(0,0,t*1.7);"
	db    "}else if(t>=30.01){"
	db     "e-=vec3(0,t,t*2.4);"
	db     "a1*=-.8;"
	db     "a3+=2.;"
	db    "}"
	db   "}"
	db   "a3=0;"
	db  "}else if(t<101.3){" ; Growing tube & sphori
	db   "h3=vec3(1.,.65,.6);"
	db   "o2=1;"
	db   "e=vec3(0,(t-56.3)*.16,-5.5);" ; Camera
	db   "if(t<59.3)e.z+=pow(1.-(t-56.3)/3.,2.)*3.;"
	db   "if((t>=57.9&&t<60.)||(t>=61.7&&t<63.8)){"
	db    "e+=vec3(0,1,1);"
	db    "e.x=-e.x;"
	db   "}"
	db   "if(t>=63.8)e.y+=2.;"
	db   "er=0;"
	db   "a1=sin(t*.06)*.16;"
	db   "a2=sin(t*.1)*.2;"
	db   "a3=0;"
	db   "if(t>=67.5&&t<71.3){"
	db    "a1*=-1.5;"
	db    "a2=-a2;"
	db    "a3+=.2;"
	db   "}"
	db   "if(t>75.){"
	db    "if(t<86.3){"
	db     "e+=vec3(0,0,1);"
	db     "if(t<78.7||t>=82.5){"
	db      "e+=vec3(0,.4,0);"
	db      "a2=-a2;"
	db      "a3+=.2;"
	db     "}else{"
	db      "e.x-=.2;"
	db      "a3=-a3;"
	db     "}"
	db    "}else{" ; just sphori at this point
	db     "e+=vec3(0,(t-86.3)*.4,0);"
	db     "a1+=.2;"
	db     "a3-=.2;"
	db     "if(t>=93.8){"
	db      "if(t<97.5){"
	db       "e-=vec3(0,0,4);"
	db       "a3*=-.9;"
	db      "}else{"
	db       "e=vec3(0,12.35+t*.3,-2)+vec3(0,-.06,1.5)*pow(max(min((t-99.3)/2.,1.),0.),2.);"
	db       "a3*=.6;"
	db      "}"
	db     "}"
	db    "}"
	db   "}"
	db  "}else{" ; Amoeba & tori
	db   "h3=vec3(.7,.9,.5);"
	db   "o2=3;"
	db   "e=vec3(0,0,-8.+5.*pow(1.-min((t-101.3)/3.,1.),2.));"
	db   "er=0;"
	db   "a1=cos(t*.54)*.06;"
	db   "a2=cos(t*.58)*.12;"
	db   "a3=.4;"
	db   "if(t<108.8||t>=116.3){"
	db    "e.z+=sin(t*1.53);"
	db    "if(t>=123.9){"
	db     "if(t>=131.3){"
	db      "e.z-=pow((t-131.3)/4.,2.)*15.;"
	db      "a3*=-1.4;"
	db     "}"
	db    "}else if(t>=116.3){"
	db     "e-=vec3(0,1,1.3);"
	db     "a1=-a1;"
	db     "a3*=-.8;"
	db    "}"
	db   "}else{"
	db    "e.y=3.-(t-108.8)*.8;"
	db   "}"
	db  "}"
	db  "float g=0.,d,d2;"
	; Camera
	db  "v=vec3(v.x,v.y*cos(a1)+v.z*sin(a1),v.z*cos(a1)-v.y*sin(a1));" ; Rotation
	db  "v=vec3(v.x*cos(a3)+v.y*sin(a3),v.y*cos(a3)-v.x*sin(a3),v.z);"
	db  "v=vec3(v.x*cos(a2)+v.z*sin(a2),v.y,v.z*cos(a2)-v.x*sin(a2));"
	db  "e-=vec3(sin(a2)*er,0,cos(a2)*er);"
	db  "vec3 p=e,c=h3*((v.z+.5)*.5)+.2*pow(max(dot(v,vec3(0,0,1))*1.01,0.),10.);"
	db  "while(g<1.&&max(max(c.x,c.y),c.z)>0){" ; Sphere trace
	db   "g=length(p-e)/15.;" ; Fog
	; Object 1
	db   "d=f(o1,p);" ; Calculate step size
	db   "if(d<=0.){"
	db    "vec3 n=normalize(vec3(f(o1,p+vec3(.01,0,0))-d,f(o1,p+vec3(0,.01,0))-d,f(o1,p+vec3(0,0,.01))-d));"
	db    "c-=(1.-g)*(1.-pow(1.-max(dot(n,-v),0.),15.))*.04;" ; Intersection
	db   "}"
	; Object 2
	db   "d2=f(o2,p);" ; Calculate step size
	db   "if(d2<=0.){"
	db    "vec3 n=normalize(vec3(f(o2,p+vec3(.01,0,0))-d2,f(o2,p+vec3(0,.01,0))-d2,f(o2,p+vec3(0,0,.01))-d2));"
	db    "c-=(1.-g)*(1.-pow(1.-max(dot(n,-v),0.),15.))*.04;" ; Intersection
	db   "}"
	; Step
	db   "d=min(d,d2);"
	db   "if(d<.005)d=.005;" ; Limit step size
	db   "p+=v*d;"
	db  "}"
	; Write
	db  "c=max(c,0.);"
	db  "if(t>19.5&&t<22.5)c*=1.-min((t-19.5)/3.,1.);"
	db  "if(t>22.5&&t<26.5)c=min(c+pow((26.5-t)*.25,3.),1.);"
	db  "if(t>52.55&&t<56.3)c*=1.-min((t-52.55)/3.75,1.);"
	db  "if(t>99.3&&t<101.3)c*=1.-pow(min((t-99.3)/2.,1.),2.);"
	db  "if(t>133.2)c*=1.-pow(min((t-133.2)/2.8,1.),2.);"
	; Scanlines etc.
;	db  "c*=sin(gl_FragCoord.y/1080.*1000.)*.05+.95-length(gl_FragCoord.xy-vec2(960,540))/1920.;" ; (1080p)
;	db  "c*=sin(gl_FragCoord.y/900.*1000.)*.05+.95-length(gl_FragCoord.xy-vec2(720,450))/1440.;" ; (1440x900)
	db  "c*=sin(gl_FragCoord.y/720.*1000.)*.05+.95-length(gl_FragCoord.xy-vec2(640,360))/1280.;" ; (720p)
;	db  "c*=sin(gl_FragCoord.y/768.*1000.)*.05+.95-length(gl_FragCoord.xy-vec2(512,384))/1024.;" ; (1024x768)
;	db  "c*=sin(gl_FragCoord.y/600.*1000.)*.05+.95-length(gl_FragCoord.xy-vec2(400,300))/800.;" ; (800x600)
;	db  "c*=sin(gl_FragCoord.y/480.*1000.)*.05+.95-length(gl_FragCoord.xy-vec2(320,240))/480.;" ; (480p)
	db  "gl_FragColor=vec4(c,1);" ; Write pixel
	db "}",0
sn_glCreateProgram: ; Names of shader procs
	db "glCreateProgram",0
sn_glCreateShader:
	db "glCreateShader",0
sn_glShaderSource:
    db "glShaderSource",0
sn_glCompileShader:
    db "glCompileShader",0
sn_glAttachShader:
    db "glAttachShader",0
sn_glLinkProgram:
    db "glLinkProgram",0
sn_glUseProgram:
    db "glUseProgram",0
%endif
%ifdef _4K_DIAGNOSTICS_
dg_title:
	db "Diagnostic Update",0
dg_resize:
	db "Screen resized successfully.",0
dg_wcreate:
	db "Window created successfully.",0
dg_spcreate:
	db "Shader program created successfully.",0
dg_svc:
	db "Vertex shader initialized successfully.",0
dg_sfc:
	db "Fragment shader initialized successfully.",0
dg_intro:
	db "Intro initialized successfully.",0
%endif
