texture.monkey2 16 KB

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