texture.monkey2 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. Namespace mojo.graphics
  2. Using std.resource
  3. Private
  4. 'fake it for now!
  5. const GL_RGBA32F:= $8814
  6. const GL_RGB32F:= $8815
  7. const GL_RGBA16F:= $881A
  8. const GL_RGB16F:= $881B
  9. const GL_DEPTH_COMPONENT32F:=$8CAC
  10. Function IsPow2:Bool( w:Int,h:Int )
  11. Local tw:=Log2( w ),th:=Log2( h )
  12. Return tw=Round( tw ) And th=Round( th )
  13. End
  14. Function IsDepth:Bool( format:PixelFormat )
  15. Return format=PixelFormat.Depth32
  16. End
  17. Function glInternalFormat:GLenum( format:PixelFormat )
  18. Select format
  19. Case PixelFormat.A8 Return GL_ALPHA
  20. Case PixelFormat.I8 Return GL_LUMINANCE
  21. Case PixelFormat.IA8 Return GL_LUMINANCE_ALPHA
  22. Case PixelFormat.RGB8 Return GL_RGB
  23. Case PixelFormat.RGBA8 Return GL_RGBA
  24. ' Case PixelFormat.RGBA16F Return GL_RGBA
  25. Case PixelFormat.RGBA32F Return BBGL_ES ? GL_RGBA Else GL_RGBA32F
  26. Case PixelFormat.Depth32 Return GL_DEPTH_COMPONENT
  27. End
  28. RuntimeError( "Invalid PixelFormat" )
  29. Return GL_RGBA
  30. End
  31. Function glFormat:GLenum( format:PixelFormat )
  32. Select format
  33. Case PixelFormat.A8 Return GL_ALPHA
  34. Case PixelFormat.I8 Return GL_LUMINANCE
  35. Case PixelFormat.IA8 Return GL_LUMINANCE_ALPHA
  36. Case PixelFormat.RGB8 Return GL_RGB
  37. Case PixelFormat.RGBA8 Return GL_RGBA
  38. ' Case PixelFormat.RGBA16F Return GL_RGBA
  39. Case PixelFormat.RGBA32F Return GL_RGBA
  40. Case PixelFormat.Depth32 Return GL_DEPTH_COMPONENT
  41. End
  42. RuntimeError( "Invalid PixelFormat" )
  43. Return GL_RGBA
  44. End
  45. Function glType:GLenum( format:PixelFormat )
  46. Select format
  47. Case PixelFormat.A8 Return GL_UNSIGNED_BYTE
  48. Case PixelFormat.I8 Return GL_UNSIGNED_BYTE
  49. Case PixelFormat.IA8 Return GL_UNSIGNED_BYTE
  50. Case PixelFormat.RGB8 Return GL_UNSIGNED_BYTE
  51. Case PixelFormat.RGBA8 Return GL_UNSIGNED_BYTE
  52. ' Case PixelFormat.RGBA16F Return GL_HALF_FLOAT
  53. Case PixelFormat.RGBA32F Return GL_FLOAT
  54. Case PixelFormat.Depth32 Return GL_UNSIGNED_INT
  55. End
  56. RuntimeError( "Invalid PixelFormat" )
  57. Return GL_UNSIGNED_BYTE
  58. End
  59. Function ClearTexImage2D( glTarget:GLenum,width:Int,height:Int,format:PixelFormat,color:Color )
  60. glCheck()
  61. Local gliformat:=glInternalFormat( format )
  62. Local glformat:=glFormat( format )
  63. Local gltype:=glType( format )
  64. glTexImage2D( glTarget,0,gliformat,width,height,0,glformat,gltype,Null )
  65. If Not IsDepth( format )
  66. Local image:=New Pixmap( width,1,format )
  67. image.Clear( color )
  68. For Local iy:=0 Until height
  69. glTexSubImage2D( glTarget,0,0,iy,width,1,glformat,gltype,image.Data )
  70. Next
  71. glFlush() 'macos nvidia bug!
  72. Endif
  73. glCheck()
  74. End
  75. Function UploadTexImage2D( glTarget:GLenum,image:Pixmap,mipmap:Bool,envface:Bool )
  76. glCheck()
  77. Local format:=image.Format
  78. Local gliformat:=glInternalFormat( format )
  79. Local glformat:=glFormat( format )
  80. Local gltype:=glType( format )
  81. Local mip:=0
  82. Repeat
  83. Local width:=image.Width,height:=image.Height
  84. If envface
  85. For Local y:=0 until height
  86. Local p:=image.PixelPtr( 0,y )
  87. 'write miplevel to alpha!
  88. For Local x:=0 Until width
  89. p[x*4+3]=mip
  90. Next
  91. Next
  92. Endif
  93. If image.Pitch=width * image.Depth
  94. glTexImage2D( glTarget,mip,gliformat,width,height,0,glformat,gltype,image.Data )
  95. glCheck()
  96. Else
  97. glTexImage2D( glTarget,mip,gliformat,width,height,0,glformat,gltype,Null )
  98. glCheck()
  99. For Local y:=0 Until height
  100. glTexSubImage2D( glTarget,mip,0,y,width,1,glformat,gltype,image.PixelPtr( 0,y ) )
  101. glCheck()
  102. Next
  103. Endif
  104. glFlush() 'macos nvidia bug!
  105. If Not mipmap Or Not envface Exit
  106. If image.Width=1 And image.Height=1 Exit
  107. image=image.MipHalve()
  108. mip+=1
  109. Forever
  110. If mipmap And Not envface glGenerateMipmap( glTarget )
  111. glCheck()
  112. End
  113. Public
  114. #rem monkeydoc Texture flags.
  115. | TextureFlags | Description
  116. |:--------------|:-----------
  117. | WrapS | Wrap S texture coordinates
  118. | WrapT | Wrap T texture coordinates
  119. | WrapST | Wrap bot S and T coordinates
  120. | Filter | Enable magnification filtering
  121. | Mipmap | Enable minification mipmapping, and minification filtering if Filter enabled.
  122. | FilterMipmap | Enable both filterin and mipmapping.
  123. | Dynamic | The texture contents are regularly updated and don't need to be preserved.
  124. | Cubemap | The texture is a cubmap.
  125. #end
  126. Enum TextureFlags
  127. None= $0000
  128. WrapS= $0001
  129. WrapT= $0002
  130. Filter= $0004
  131. Mipmap= $0008
  132. Dynamic= $0100
  133. Cubemap= $0200
  134. WrapST= WrapS|WrapT
  135. FilterMipmap= Filter|Mipmap
  136. End
  137. #rem monketdoc @hidden
  138. #end
  139. Enum CubeFace
  140. PositiveX
  141. NegativeX
  142. PositiveY
  143. NegativeY
  144. PositiveZ
  145. NegativeZ
  146. End
  147. #rem monkeydoc The Texture class.
  148. The "MOJO_TEXTURE_MAX_ANISOTROPY" config setting control the max anisotropy of mipmapped textures.
  149. #end
  150. Class Texture Extends Resource
  151. #rem monkeydoc Creates a new texture.
  152. The "MOJO_TEXTURE_MAX_ANISOTROPY" config setting control the max anisotropy of mipmapped textures.
  153. #end
  154. Method New( width:Int,height:Int,format:PixelFormat,flags:TextureFlags )
  155. Init( width,height,format,flags,Null )
  156. End
  157. Method New( pixmap:Pixmap,flags:TextureFlags )
  158. If flags & TextureFlags.Cubemap
  159. 'Nasty: cubemap mips always have mip level in alpha channel for env maps
  160. '
  161. If flags & TextureFlags.Mipmap And pixmap.Format<>PixelFormat.RGBA8
  162. pixmap=pixmap.Convert( PixelFormat.RGBA8 )
  163. Endif
  164. Local size:=pixmap.Size
  165. If size.x=size.y '1x1?
  166. Init( size.x,size.y,pixmap.Format,flags,Null )
  167. For Local i:=0 Until 6
  168. _cubeFaces[i].PastePixmap( pixmap,0,0 )
  169. Next
  170. Else If size.x/4*3=size.y '4x3?
  171. size=New Vec2i( size.x/4,size.y/3 )
  172. Init( size.x,size.y,pixmap.Format,flags,Null )
  173. Const offsets:=New Int[]( 2,1, 0,1, 1,0, 1,2, 1,1, 3,1 )
  174. For Local i:=0 Until 6
  175. Local face:=pixmap.Window( offsets[i*2]*size.x,offsets[i*2+1]*size.y,size.x,size.y )
  176. _cubeFaces[i].PastePixmap( face,0,0 )
  177. Next
  178. Else
  179. RuntimeError( "Invalid Cubemap image size" )
  180. Endif
  181. Return
  182. Endif
  183. If Not (flags & TextureFlags.Dynamic)
  184. Init( pixmap.Width,pixmap.Height,pixmap.Format,flags,pixmap )
  185. Else
  186. Init( pixmap.Width,pixmap.Height,pixmap.Format,flags,Null )
  187. PastePixmap( pixmap,0,0 )
  188. Endif
  189. End
  190. Property Size:Vec2i()
  191. Return _size
  192. End
  193. Property Width:Int()
  194. Return _size.x
  195. End
  196. Property Height:Int()
  197. Return _size.y
  198. End
  199. Property Format:PixelFormat()
  200. Return _format
  201. End
  202. Property Flags:TextureFlags()
  203. Assert( Not _cubeMap )
  204. Return _flags
  205. Setter( flags:TextureFlags )
  206. Assert( Not _cubeMap )
  207. Local mask:=TextureFlags.WrapS|TextureFlags.WrapT|TextureFlags.Filter|TextureFlags.Mipmap
  208. _flags=(_flags & ~mask) | (flags & mask)
  209. If _flags & TextureFlags.Mipmap _dirty|=Dirty.Mipmaps Else _dirty&=~Dirty.Mipmaps
  210. _dirty|=Dirty.TexParams
  211. End
  212. Property ManagedPixmap:Pixmap()
  213. Return _managed
  214. End
  215. Method PastePixmap( pixmap:Pixmap,x:Int,y:Int )
  216. If _managed
  217. _managed.Paste( pixmap,x,y )
  218. _dirty|=Dirty.TexImage
  219. Return
  220. Endif
  221. _dirty&=~Dirty.Mipmaps 'don't bother generating mipmaps when validating texture...
  222. glActiveTexture( GL_TEXTURE7 )
  223. glBindTexture( _cubeMap ? _cubeMap._glTarget Else _glTarget,ValidateGLTexture() )
  224. If pixmap.Pitch=pixmap.Width*pixmap.Depth
  225. glTexSubImage2D( _glTarget,0,x,y,pixmap.Width,pixmap.Height,glFormat( _format ),GL_UNSIGNED_BYTE,pixmap.Data )
  226. Else
  227. For Local iy:=0 Until pixmap.Height
  228. glTexSubImage2D( _glTarget,0,x,y+iy,pixmap.Width,1,glFormat( _format ),GL_UNSIGNED_BYTE,pixmap.PixelPtr( 0,iy ) )
  229. Next
  230. Endif
  231. If _flags & TextureFlags.Mipmap _dirty|=Dirty.Mipmaps
  232. End
  233. Method GetCubeFace:Texture( face:CubeFace )
  234. If _cubeFaces Return _cubeFaces[ Cast<Int>( face ) ]
  235. Return Null
  236. End
  237. Function Load:Texture( path:String,flags:TextureFlags=TextureFlags.FilterMipmap,flipNormalY:Bool=False )
  238. Local pixmap:=Pixmap.Load( path,,True )
  239. If Not pixmap Return Null
  240. If flipNormalY
  241. For Local y:=0 Until pixmap.Height
  242. For Local x:=0 Until pixmap.Width
  243. pixmap.SetPixelARGB( x,y,pixmap.GetPixelARGB( x,y ) ~ $ff00 )
  244. Next
  245. Next
  246. Endif
  247. Local texture:=New Texture( pixmap,flags )
  248. Return texture
  249. End
  250. Function LoadNormal:Texture( path:String,textureFlags:TextureFlags,specular:String,specularScale:Float=1,flipNormalY:Bool=True )
  251. path=RealPath( path )
  252. specular=specular ? RealPath( specular ) Else ""
  253. Local pnorm:=Pixmap.Load( path,,False )
  254. If Not pnorm Return Null
  255. Local pspec:=Pixmap.Load( specular )
  256. Local yxor:=flipNormalY ? $ff00 Else 0
  257. If pspec And pspec.Width=pnorm.Width And pspec.Height=pnorm.Height
  258. For Local y:=0 Until pnorm.Height
  259. For Local x:=0 Until pnorm.Width
  260. Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
  261. Local s:=(pspec.GetPixelARGB( x,y ) Shr 16) & $ff
  262. n=n & $ffffff00 | Clamp( Int( specularScale * s ),0,255 )
  263. pnorm.SetPixelARGB( x,y,n )
  264. Next
  265. Next
  266. Else
  267. Local g:=Clamp( Int( specularScale * 255.0 ),1,255 )
  268. For Local y:=0 Until pnorm.Height
  269. For Local x:=0 Until pnorm.Width
  270. Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
  271. n=n & $ffffff00 | g
  272. pnorm.SetPixelARGB( x,y,n )
  273. Next
  274. Next
  275. Endif
  276. Local texture:=New Texture( pnorm,Null )
  277. Return texture
  278. End
  279. Function ColorTexture:Texture( color:Color )
  280. Global _cache:=New Map<Color,Texture>
  281. Local texture:=_cache[color]
  282. If Not texture
  283. Local pixmap:=New Pixmap( 1,1 )
  284. pixmap.Clear( color )
  285. texture=New Texture( pixmap,Null )
  286. _cache[color]=texture
  287. Endif
  288. Return texture
  289. End
  290. Function FlatNormal:Texture()
  291. Return ColorTexture( New Color( .5,.5,1 ) )
  292. End
  293. '***** INTERNAL *****
  294. #rem monkeydoc @hidden
  295. #end
  296. Property GLTarget:GLenum()
  297. Return _glTarget
  298. End
  299. #rem monkeydoc @hidden
  300. #end
  301. Method Modified( r:Recti )
  302. If _managed
  303. ' Print "Texture Modified - Update managed"
  304. glReadPixels( r.X,r.Y,r.Width,r.Height,GL_RGBA,GL_UNSIGNED_BYTE,_managed.PixelPtr( r.X,r.Y ) )
  305. Endif
  306. If _flags & TextureFlags.Mipmap _dirty|=Dirty.Mipmaps
  307. End
  308. #rem monkeydoc @hidden
  309. #end
  310. Method Bind( unit:Int )
  311. ' Assert( unit<7 And Not _cubeMap )
  312. Local gltex:=ValidateGLTexture()
  313. glActiveTexture( GL_TEXTURE0+unit )
  314. glBindTexture( _glTarget,gltex )
  315. End
  316. #rem monkeydoc @hidden
  317. #end
  318. Method ValidateGLTexture:GLuint()
  319. If _cubeMap Return _cubeMap.ValidateGLTexture()
  320. If _discarded Return 0
  321. If _retroMode<>glRetroMode
  322. _dirty|=Dirty.TexParams
  323. _retroMode=glRetroMode
  324. Endif
  325. If _glSeq=glGraphicsSeq And Not _dirty Return _glTexture
  326. glCheck()
  327. If _glSeq<>glGraphicsSeq
  328. glGenTextures( 1,Varptr _glTexture )
  329. _glSeq=glGraphicsSeq
  330. _dirty=Dirty.All
  331. Endif
  332. glActiveTexture( GL_TEXTURE7 )
  333. glBindTexture( _glTarget,_glTexture )
  334. If _dirty & Dirty.TexParams
  335. If _flags & TextureFlags.WrapS
  336. glTexParameteri( _glTarget,GL_TEXTURE_WRAP_S,GL_REPEAT )
  337. Else
  338. glTexParameteri( _glTarget,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE )
  339. Endif
  340. If _flags & TextureFlags.WrapT
  341. glTexParameteri( _glTarget,GL_TEXTURE_WRAP_T,GL_REPEAT )
  342. Else
  343. glTexParameteri( _glTarget,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE )
  344. Endif
  345. If _retroMode
  346. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
  347. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST )
  348. Else
  349. If _flags & TextureFlags.Mipmap
  350. If _flags & TextureFlags.Filter
  351. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_LINEAR )
  352. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR )
  353. Else
  354. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
  355. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST )
  356. Endif
  357. Else If _flags & TextureFlags.Filter
  358. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_LINEAR )
  359. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_LINEAR )
  360. Else
  361. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
  362. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST )
  363. Endif
  364. If _flags & TextureFlags.Mipmap And Not (_flags & TextureFlags.Cubemap)
  365. ' If glexts.GL_texture_filter_anisotropic
  366. Local max:Int=0
  367. glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY,Varptr max )
  368. Local n:=Min( Int(GetConfig( "MOJO_TEXTURE_MAX_ANISOTROPY",max )),max )
  369. glTexParameteri( _glTarget,GL_TEXTURE_MAX_ANISOTROPY,n )
  370. ' Endif
  371. Endif
  372. Endif
  373. glCheck()
  374. Endif
  375. If _dirty & Dirty.TexImage
  376. Local mipmap:=(_flags & TextureFlags.Mipmap)<>0
  377. Select _glTarget
  378. Case GL_TEXTURE_2D
  379. If _managed
  380. UploadTexImage2D( _glTarget,_managed,mipmap,False )
  381. _dirty&=~Dirty.Mipmaps
  382. else
  383. ClearTexImage2D( _glTarget,_size.x,_size.y,_format,Color.Black )
  384. If mipmap _dirty|=Dirty.Mipmaps
  385. Endif
  386. Case GL_TEXTURE_CUBE_MAP
  387. Local envface:=mipmap
  388. If _cubeFaces[0]._managed
  389. For Local i:=0 Until 6
  390. Local face:=_cubeFaces[i]
  391. UploadTexImage2D( face._glTarget,face._managed,mipmap,envface )
  392. Next
  393. _dirty&=~Dirty.Mipmaps
  394. Else
  395. For Local i:=0 Until 6
  396. Local face:=_cubeFaces[i]
  397. ClearTexImage2D( face._glTarget,face._size.x,face._size.y,face._format,Color.Black )
  398. Next
  399. If mipmap _dirty|=Dirty.Mipmaps
  400. Endif
  401. End
  402. Endif
  403. If _dirty & Dirty.Mipmaps
  404. If _flags & TextureFlags.Mipmap
  405. glGenerateMipmap( _glTarget )
  406. glCheck()
  407. Endif
  408. End
  409. _dirty=Null
  410. glCheck()
  411. Return _glTexture
  412. End
  413. Protected
  414. #rem monkeydoc @hidden
  415. #end
  416. Method OnDiscard() Override
  417. If _cubeMap return
  418. If _glSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
  419. _discarded=True
  420. _managed=Null
  421. _glTexture=0
  422. _glSeq=0
  423. End
  424. #rem monkeydoc @hidden
  425. #end
  426. Method OnFinalize() Override
  427. If _cubeMap return
  428. If _glSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
  429. End
  430. Private
  431. Enum Dirty
  432. TexParams= 1
  433. TexImage= 2
  434. Mipmaps= 4
  435. All= 7
  436. End
  437. 'Global _boundSeq:Int
  438. 'Global _bound:=New GLuint[8]
  439. Field _size:Vec2i
  440. Field _format:PixelFormat
  441. Field _flags:TextureFlags
  442. Field _managed:Pixmap
  443. Field _cubeMap:Texture
  444. Field _cubeFaces:Texture[]
  445. Field _glTarget:GLenum
  446. Field _discarded:Bool
  447. Field _retroMode:Bool
  448. Field _dirty:Dirty
  449. Field _glSeq:Int
  450. Field _glTexture:GLuint
  451. Method Init( width:Int,height:Int,format:PixelFormat,flags:TextureFlags,managed:Pixmap )
  452. If flags & TextureFlags.Cubemap Assert( width=height,"Cubemaps must be square" )
  453. _size=New Vec2i( width,height )
  454. _format=format
  455. _flags=flags
  456. _glTarget=_flags & TextureFlags.Cubemap ? GL_TEXTURE_CUBE_MAP Else GL_TEXTURE_2D
  457. #If Not __DESKTOP_TARGET__
  458. If Not IsPow2( _size.x,_size.y ) _flags&=~TextureFlags.Mipmap
  459. #Endif
  460. If _flags & TextureFlags.Cubemap
  461. _cubeFaces=New Texture[6]
  462. For Local i:=0 Until 6
  463. Local face:=New Texture( _size.x,_size.y,_format,_flags & ~TextureFlags.Cubemap )
  464. face._glTarget=GL_TEXTURE_CUBE_MAP_POSITIVE_X+i
  465. face._cubeMap=Self
  466. _cubeFaces[i]=face
  467. Next
  468. Return
  469. Endif
  470. If Not (_flags & TextureFlags.Dynamic)
  471. If Not managed managed=New Pixmap( width,height,format )
  472. _managed=managed
  473. Endif
  474. End
  475. End
  476. Class ResourceManager Extension
  477. Method OpenTexture:Texture( path:String,flags:TextureFlags=Null )
  478. Local slug:="Texture:name="+StripDir( StripExt( path ) )+"&flags="+Int( flags )
  479. Local texture:=Cast<Texture>( OpenResource( slug ) )
  480. If texture Return texture
  481. Local pixmap:=OpenPixmap( path,Null,True )
  482. If Not pixmap Return Null
  483. texture=New Texture( pixmap,flags )
  484. AddResource( slug,texture )
  485. Return texture
  486. End
  487. End