shader.monkey2 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. Namespace mojo.graphics
  2. #Import "shaders/@/shaders"
  3. Internal
  4. Const A_POSITION:=0
  5. Const A_TEXCOORD0:=1
  6. Const A_TEXCOORD1:=2
  7. Const A_COLOR:=3
  8. Const A_NORMAL:=4
  9. Const A_TANGENT:=5
  10. Const A_WEIGHTS:=6
  11. Const A_BONES:=7
  12. Private
  13. Class GLUniform
  14. Field name:String
  15. Field location:Int
  16. Field texunit:Int
  17. Field size:Int
  18. Field type:Int
  19. Field block:Int
  20. Field uniformId:Int
  21. Method New( name:String,location:Int,texunit:Int,size:Int,type:Int )
  22. Self.name=name
  23. Self.location=location
  24. Self.texunit=texunit
  25. Self.size=size
  26. Self.type=type
  27. If name.StartsWith( "g_" )
  28. name=name.Slice( 2 )
  29. block=0
  30. Else If name.StartsWith( "r_" )
  31. name=name.Slice( 2 )
  32. block=1
  33. Else If name.StartsWith( "i_" )
  34. name=name.Slice( 2 )
  35. block=2
  36. Else If name.StartsWith( "m_" )
  37. name=name.Slice( 2 )
  38. block=3
  39. Else If name.StartsWith( "x_" )
  40. name=name.Slice( 2 )
  41. block=4
  42. Endif
  43. uniformId=UniformBlock.GetUniformId( name,block )
  44. End
  45. End
  46. Class GLProgram
  47. Field _glprogram:GLuint
  48. Field _uniforms:=New GLUniform[8][]
  49. Field _textures:=New GLUniform[8][]
  50. Field _ublockSeqs:=New Int[8]
  51. Field _glRetroSeq:Int
  52. Method New( glprogram:GLuint )
  53. _glprogram=glprogram
  54. Local uniforms:=New Stack<GLUniform>[8]
  55. Local textures:=New Stack<GLUniform>[8]
  56. For Local i:=0 Until 8
  57. uniforms[i]=New Stack<GLUniform>
  58. textures[i]=New Stack<GLUniform>
  59. Next
  60. Local n:Int
  61. glGetProgramiv( _glprogram,GL_ACTIVE_UNIFORMS,Varptr n )
  62. Local size:Int,type:UInt,length:Int,nameBuf:=New Byte[256],texunit:=0
  63. For Local i:=0 Until n
  64. glGetActiveUniform( _glprogram,i,nameBuf.Length,Varptr length,Varptr size,Varptr type,Cast<GLchar Ptr>( nameBuf.Data ) )
  65. Local name:=String.FromCString( nameBuf.Data )
  66. Local i:=name.Find( "[" )
  67. If i<>-1
  68. name=name.Slice( 0,i )
  69. Endif
  70. Local location:=glGetUniformLocation( _glprogram,name )
  71. If location=-1 Continue 'IE fix...
  72. Local uniform:=New GLUniform( name,location,texunit,size,type )
  73. uniforms[uniform.block].Push( uniform )
  74. Select type
  75. Case GL_SAMPLER_2D,GL_SAMPLER_CUBE
  76. textures[uniform.block].Push( uniform )
  77. texunit+=1
  78. End
  79. Next
  80. For Local i:=0 until 8
  81. _uniforms[i]=uniforms[i].ToArray()
  82. _textures[i]=textures[i].ToArray()
  83. _ublockSeqs[i]=-1
  84. Next
  85. End
  86. Property GLProgram:GLuint()
  87. Return _glprogram
  88. End
  89. Method ValidateUniforms( ublocks:UniformBlock[] )
  90. For Local i:=0 Until 8
  91. Local ublock:=ublocks[ i ]
  92. If Not ublock Or ublock.Seq=_ublockSeqs[i] Continue
  93. _ublockSeqs[i]=ublock.Seq
  94. For Local u:=Eachin _uniforms[i]
  95. Select u.type
  96. Case GL_BOOL
  97. glUniform1i( u.location,ublock.GetInt( u.uniformId ) )
  98. Case GL_INT
  99. glUniform1i( u.location,ublock.GetInt( u.uniformId ) )
  100. Case GL_FLOAT
  101. glUniform1f( u.location,ublock.GetFloat( u.uniformId ) )
  102. Case GL_FLOAT_VEC2
  103. glUniform2fv( u.location,1,ublock.GetVec2fv( u.uniformId ) )
  104. Case GL_FLOAT_VEC3
  105. glUniform3fv( u.location,1,ublock.GetVec3fv( u.uniformId ) )
  106. Case GL_FLOAT_VEC4
  107. glUniform4fv( u.location,1,ublock.GetVec4fv( u.uniformId ) )
  108. Case GL_FLOAT_MAT3
  109. glUniformMatrix3fv( u.location,1,False,ublock.GetMat3fv( u.uniformId ) )
  110. Case GL_FLOAT_MAT4
  111. Local size:=u.size
  112. If size>1 size=ublock.GetMat4fArray( u.uniformId ).Length
  113. glUniformMatrix4fv( u.location,size,False,ublock.GetMat4fv( u.uniformId ) )
  114. Case GL_SAMPLER_2D,GL_SAMPLER_CUBE
  115. glUniform1i( u.location,u.texunit )
  116. End
  117. Next
  118. Next
  119. For Local i:=0 Until 8
  120. If Not _textures[i] Continue
  121. For Local u:=Eachin _textures[i]
  122. Local tex:=ublocks[i].GetTexture( u.uniformId )
  123. If Not tex
  124. Print( "Can't bind shader texture uniform '"+u.name+"' - no texture!" )
  125. Continue
  126. Endif
  127. tex.Bind( u.texunit )
  128. Next
  129. Next
  130. glActiveTexture( GL_TEXTURE0 )
  131. End
  132. End
  133. Public
  134. #rem monkeydoc The Shader class.
  135. #end
  136. Class Shader
  137. #rem monkeydoc Creates a new shader.
  138. #end
  139. Method New( name:String,source:String,defs:String )
  140. _name=name
  141. _source=source
  142. For Local def:=Eachin defs.Replace( ";","~n" ).Split( "~n" )
  143. def=def.Trim()
  144. If Not def Continue
  145. if Not def.Contains( " " ) def+=" 1"
  146. _defs+="#define "+def+"~n"
  147. Next
  148. EnumPasses()
  149. End
  150. #rem monkeydoc The shader name.
  151. #end
  152. Property Name:String()
  153. Return _name
  154. End
  155. #rem monkeydoc The shader source code.
  156. #end
  157. Property Source:String()
  158. Return _source
  159. End
  160. #rem monkeydoc The renderpasses the shader is valid for.
  161. #end
  162. Property RenderPasses:Int[]()
  163. Return _rpasses
  164. End
  165. #rem monkeydoc Renderpass bitmask.
  166. #end
  167. Property RenderPassMask:Int()
  168. Return _rpassMask
  169. End
  170. #rem monkeydoc Shader global uniforms.
  171. #end
  172. Property Uniforms:UniformBlock()
  173. If Not _uniforms _uniforms=New UniformBlock( 0 )
  174. Return _uniforms
  175. End
  176. '***** INTERNAL *****
  177. #rem monkeydoc @hidden
  178. #end
  179. Method Bind( renderPass:Int )
  180. If _glSeq<>glGraphicsSeq
  181. _glSeq=glGraphicsSeq
  182. Rebuild()
  183. Endif
  184. glUseProgram( _programs[renderPass].GLProgram )
  185. End
  186. #rem monkeydoc @hidden
  187. #end
  188. Method ValidateUniforms( renderPass:Int,ublocks:UniformBlock[] )
  189. _programs[renderPass].ValidateUniforms( ublocks )
  190. End
  191. #rem monkeydoc Gets a shader with a given name.
  192. #end
  193. Function GetShader:Shader( name:String,defs:String="" )
  194. Local tag:=name+";"+defs
  195. If _cache.Contains( tag ) Return _cache[tag]
  196. local source:=LoadString( "asset::shaders/"+name+".glsl" )
  197. Local shader:=source ? New Shader( name,source,defs ) Else Null
  198. _cache[tag]=shader
  199. Return shader
  200. End
  201. #rem monkeydoc Gets a shader with a given name.
  202. #end
  203. Function Open:Shader( name:String,defs:String="" )
  204. Return GetShader( name,defs )
  205. End
  206. Private
  207. Global _cache:=New StringMap<Shader>
  208. Field _name:String
  209. Field _source:String
  210. Field _defs:String
  211. Field _rpasses:Int[]
  212. Field _rpassMask:Int
  213. Field _uniforms:UniformBlock
  214. Field _programs:=New GLProgram[32]
  215. Field _glSeq:Int
  216. Method EnumPasses()
  217. Local tag:="//@renderpasses"
  218. Local tagi:=_source.Find( tag )
  219. If tagi=-1
  220. Print "Shader source:~n"+_source
  221. RuntimeError( "Can't find '"+tag+"' tag" )
  222. Endif
  223. tagi+=tag.Length
  224. Local tage:=_source.Find( "~n",tagi )
  225. If tage=-1 tage=_source.Length
  226. Local tagv:=_source.Slice( tagi,tage )
  227. Local rpasses:=tagv.Split( "," )
  228. If Not rpasses
  229. Print "Shader source:~n"+_source
  230. RuntimeError( "Invalid renderpasses value: '"+tagv+"'" )
  231. Endif
  232. _rpasses=New Int[rpasses.Length]
  233. For Local i:=0 Until rpasses.Length
  234. _rpasses[i]=Int( rpasses[i] )
  235. _rpassMask|=(1 Shl _rpasses[i])
  236. Next
  237. End
  238. 'Find common/vertex/fragment chunks
  239. '
  240. Method SplitSource:String[]( source:String )
  241. Local i0:=source.Find( "~n//@vertex" )
  242. If i0=-1
  243. Print "Shader source:~n"+source
  244. RuntimeError( "Can't find //@vertex chunk" )
  245. Endif
  246. Local i1:=source.Find( "~n//@fragment",i0+1 )
  247. If i1=-1
  248. Print "Shader source:~n"+source
  249. RuntimeError( "Can't find //@fragment chunk" )
  250. Endif
  251. Local cs:=source.Slice( 0,i0 )+"~n"
  252. Local vs:=source.Slice( i0,i1 )+"~n"
  253. Local fs:=source.Slice( i1 )+"~n"
  254. Local chunks:=New String[3]
  255. 'Find //@imports in common section
  256. i0=0
  257. Repeat
  258. i0=cs.Find( "~n//@import",i0 )
  259. If i0=-1 Exit
  260. Local i1:=cs.Find( "~n",i0+1 )
  261. If i1=-1 RuntimeError( "Malformed @import directive in shader" )
  262. Local f:=cs.Slice( i0+11,i1 ).Trim()
  263. i0=i1
  264. If Not f.StartsWith( "~q" ) Or Not f.EndsWith( "~q" )
  265. RuntimeError( "Malformed @import directive in shader" )
  266. Exit
  267. Endif
  268. f=f.Slice(1,-1)
  269. Local path:="asset::shaders/imports/"+f+".glsl"
  270. Local src:=LoadString( path )
  271. Assert( src,"Can't import shader from "+path )
  272. Local tchunks:=SplitSource( src )
  273. chunks[0]+=tchunks[0]
  274. chunks[1]+=tchunks[1]
  275. chunks[2]+=tchunks[2]
  276. ' Print "Imported "+f
  277. Forever
  278. chunks[0]+=cs
  279. chunks[1]+=vs
  280. chunks[2]+=fs
  281. Return chunks
  282. End
  283. Method Rebuild()
  284. glCheck()
  285. Local chunks:=SplitSource( _source )
  286. Local cs:=_defs+chunks[0]
  287. Local vs:=cs+chunks[1]
  288. Local fs:=cs+chunks[2]
  289. For Local rpass:=Eachin _rpasses
  290. Local defs:="#define MX2_RENDERPASS "+rpass+"~n"
  291. ' Print "~n~n*************** Vertex Shader ***************~n"+defs+vs
  292. ' Print "~n~n*************** Fragment Shader ****************~n"+defs+fs
  293. Local vshader:=glCompile( GL_VERTEX_SHADER,defs+vs )
  294. Local fshader:=glCompile( GL_FRAGMENT_SHADER,defs+fs )
  295. Local glprogram:=glCreateProgram()
  296. glAttachShader( glprogram,vshader )
  297. glAttachShader( glprogram,fshader )
  298. glDeleteShader( vshader )
  299. glDeleteShader( fshader )
  300. glBindAttribLocation( glprogram,A_POSITION,"a_Position" )
  301. glBindAttribLocation( glprogram,A_TEXCOORD0,"a_TexCoord0" )
  302. glBindAttribLocation( glprogram,A_TEXCOORD1,"a_TexCoord1" )
  303. glBindAttribLocation( glprogram,A_COLOR,"a_Color" )
  304. glBindAttribLocation( glprogram,A_NORMAL,"a_Normal" )
  305. glBindAttribLocation( glprogram,A_TANGENT,"a_Tangent" )
  306. glBindAttribLocation( glprogram,A_WEIGHTS,"a_Weights" )
  307. glBindAttribLocation( glprogram,A_BONES,"a_Bones" )
  308. glLink( glprogram )
  309. Local program:=New GLProgram( glprogram )
  310. _programs[rpass]=program
  311. Next
  312. glCheck()
  313. End
  314. End