texture.monkey2 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  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. Local mask:=TextureFlags.WrapS|TextureFlags.WrapT|TextureFlags.Filter|TextureFlags.Mipmap
  220. _flags=(_flags & ~mask) | (flags & mask)
  221. If _flags & TextureFlags.Mipmap _dirty|=Dirty.Mipmaps Else _dirty&=~Dirty.Mipmaps
  222. _dirty|=Dirty.TexParams
  223. End
  224. Property ManagedPixmap:Pixmap()
  225. Return _managed
  226. End
  227. Method PastePixmap( pixmap:Pixmap,x:Int,y:Int )
  228. If _managed
  229. _managed.Paste( pixmap,x,y )
  230. _dirty|=Dirty.TexImage
  231. Return
  232. Endif
  233. _dirty&=~Dirty.Mipmaps 'don't bother generating mipmaps when validating texture...
  234. glActiveTexture( GL_TEXTURE7 )
  235. glBindTexture( _cubeMap ? _cubeMap._glTarget Else _glTarget,ValidateGLTexture() )
  236. If pixmap.Pitch=pixmap.Width*pixmap.Depth
  237. glTexSubImage2D( _glTarget,0,x,y,pixmap.Width,pixmap.Height,glFormat( _format ),GL_UNSIGNED_BYTE,pixmap.Data )
  238. Else
  239. For Local iy:=0 Until pixmap.Height
  240. glTexSubImage2D( _glTarget,0,x,y+iy,pixmap.Width,1,glFormat( _format ),GL_UNSIGNED_BYTE,pixmap.PixelPtr( 0,iy ) )
  241. Next
  242. Endif
  243. If _flags & TextureFlags.Mipmap _dirty|=Dirty.Mipmaps
  244. End
  245. Method GetCubeFace:Texture( face:CubeFace )
  246. If _cubeFaces Return _cubeFaces[ Cast<Int>( face ) ]
  247. Return Null
  248. End
  249. Function Load:Texture( path:String,flags:TextureFlags=TextureFlags.FilterMipmap,flipNormalY:Bool=False )
  250. Local format:=PixelFormat.Unknown
  251. If flags & TextureFlags.Envmap
  252. If ExtractExt( path )=".hdr"
  253. format=PixelFormat.RGBA32F
  254. Else
  255. format=PixelFormat.RGBA8
  256. Endif
  257. Endif
  258. Local pixmap:=Pixmap.Load( path,format,True )
  259. If Not pixmap Return Null
  260. If flipNormalY
  261. For Local y:=0 Until pixmap.Height
  262. For Local x:=0 Until pixmap.Width
  263. pixmap.SetPixelARGB( x,y,pixmap.GetPixelARGB( x,y ) ~ $ff00 )
  264. Next
  265. Next
  266. Endif
  267. Local texture:=New Texture( pixmap,flags )
  268. Return texture
  269. End
  270. Function LoadNormal:Texture( path:String,textureFlags:TextureFlags,specular:String,specularScale:Float=1,flipNormalY:Bool=True )
  271. path=RealPath( path )
  272. specular=specular ? RealPath( specular ) Else ""
  273. Local pnorm:=Pixmap.Load( path,,False )
  274. If Not pnorm Return Null
  275. Local pspec:=Pixmap.Load( specular )
  276. Local yxor:=flipNormalY ? $ff00 Else 0
  277. If pspec And pspec.Width=pnorm.Width And pspec.Height=pnorm.Height
  278. For Local y:=0 Until pnorm.Height
  279. For Local x:=0 Until pnorm.Width
  280. Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
  281. Local s:=(pspec.GetPixelARGB( x,y ) Shr 16) & $ff
  282. n=n & $ffffff00 | Clamp( Int( specularScale * s ),0,255 )
  283. pnorm.SetPixelARGB( x,y,n )
  284. Next
  285. Next
  286. Else
  287. Local g:=Clamp( Int( specularScale * 255.0 ),1,255 )
  288. For Local y:=0 Until pnorm.Height
  289. For Local x:=0 Until pnorm.Width
  290. Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
  291. n=n & $ffffff00 | g
  292. pnorm.SetPixelARGB( x,y,n )
  293. Next
  294. Next
  295. Endif
  296. Local texture:=New Texture( pnorm,Null )
  297. Return texture
  298. End
  299. Function ColorTexture:Texture( color:Color )
  300. Global _cache:=New Map<Color,Texture>
  301. Local texture:=_cache[color]
  302. If Not texture
  303. Local pixmap:=New Pixmap( 1,1 )
  304. pixmap.Clear( color )
  305. texture=New Texture( pixmap,Null )
  306. _cache[color]=texture
  307. Endif
  308. Return texture
  309. End
  310. Function FlatNormal:Texture()
  311. Return ColorTexture( New Color( .5,.5,1 ) )
  312. End
  313. '***** INTERNAL *****
  314. #rem monkeydoc @hidden
  315. #end
  316. Property GLTarget:GLenum()
  317. Return _glTarget
  318. End
  319. #rem monkeydoc @hidden
  320. #end
  321. Method Modified( r:Recti )
  322. If _managed
  323. ' Print "Texture Modified - Update managed"
  324. glReadPixels( r.X,r.Y,r.Width,r.Height,GL_RGBA,GL_UNSIGNED_BYTE,_managed.PixelPtr( r.X,r.Y ) )
  325. Endif
  326. If _flags & TextureFlags.Mipmap _dirty|=Dirty.Mipmaps
  327. End
  328. #rem monkeydoc @hidden
  329. #end
  330. Method Bind( unit:Int )
  331. ' Assert( unit<7 And Not _cubeMap )
  332. Local gltex:=ValidateGLTexture()
  333. glActiveTexture( GL_TEXTURE0+unit )
  334. glBindTexture( _glTarget,gltex )
  335. End
  336. #rem monkeydoc @hidden
  337. #end
  338. Method ValidateGLTexture:GLuint()
  339. If _cubeMap Return _cubeMap.ValidateGLTexture()
  340. If _discarded Return 0
  341. If _retroMode<>glRetroMode
  342. _dirty|=Dirty.TexParams
  343. _retroMode=glRetroMode
  344. Endif
  345. If _glSeq=glGraphicsSeq And Not _dirty Return _glTexture
  346. glCheck()
  347. If _glSeq<>glGraphicsSeq
  348. glGenTextures( 1,Varptr _glTexture )
  349. _glSeq=glGraphicsSeq
  350. _dirty=Dirty.All
  351. Endif
  352. glActiveTexture( GL_TEXTURE7 )
  353. glBindTexture( _glTarget,_glTexture )
  354. If _dirty & Dirty.TexParams
  355. If _flags & TextureFlags.WrapS
  356. glTexParameteri( _glTarget,GL_TEXTURE_WRAP_S,GL_REPEAT )
  357. Else
  358. glTexParameteri( _glTarget,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE )
  359. Endif
  360. If _flags & TextureFlags.WrapT
  361. glTexParameteri( _glTarget,GL_TEXTURE_WRAP_T,GL_REPEAT )
  362. Else
  363. glTexParameteri( _glTarget,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE )
  364. Endif
  365. If _retroMode
  366. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
  367. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST )
  368. Else
  369. If _flags & TextureFlags.Mipmap
  370. If _flags & TextureFlags.Filter
  371. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_LINEAR )
  372. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR )
  373. Else
  374. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
  375. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST )
  376. Endif
  377. Else If _flags & TextureFlags.Filter
  378. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_LINEAR )
  379. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_LINEAR )
  380. Else
  381. glTexParameteri( _glTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
  382. glTexParameteri( _glTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST )
  383. Endif
  384. If _flags & TextureFlags.Mipmap And Not (_flags & (TextureFlags.Cubemap|TextureFlags.Envmap))
  385. ' If glexts.GL_texture_filter_anisotropic
  386. Local max:Int=0
  387. glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY,Varptr max )
  388. Local n:=Min( Int(GetConfig( "MOJO_TEXTURE_MAX_ANISOTROPY",max )),max )
  389. glTexParameteri( _glTarget,GL_TEXTURE_MAX_ANISOTROPY,n )
  390. ' Endif
  391. Endif
  392. Endif
  393. glCheck()
  394. Endif
  395. If _dirty & Dirty.TexImage
  396. Local mipmap:=(_flags & TextureFlags.Mipmap)<>Null
  397. Local envmap:=(_flags & TextureFlags.Envmap)<>Null
  398. Select _glTarget
  399. Case GL_TEXTURE_2D
  400. If _managed
  401. UploadTexImage2D( _glTarget,_managed,mipmap,envmap )
  402. If envmap mipmap=False
  403. else
  404. ClearTexImage2D( _glTarget,_size.x,_size.y,_format,Color.Black )
  405. Endif
  406. Case GL_TEXTURE_CUBE_MAP
  407. If _cubeFaces[0]._managed
  408. For Local i:=0 Until 6
  409. Local face:=_cubeFaces[i]
  410. UploadTexImage2D( face._glTarget,face._managed,mipmap,envmap )
  411. Next
  412. If envmap mipmap=False
  413. Else
  414. For Local i:=0 Until 6
  415. Local face:=_cubeFaces[i]
  416. ClearTexImage2D( face._glTarget,face._size.x,face._size.y,face._format,Color.Black )
  417. Next
  418. Endif
  419. End
  420. If mipmap glGenerateMipmap( _glTarget )
  421. _dirty&=~Dirty.Mipmaps
  422. Endif
  423. If _dirty & Dirty.Mipmaps
  424. If _flags & TextureFlags.Mipmap
  425. glGenerateMipmap( _glTarget )
  426. glCheck()
  427. Endif
  428. End
  429. _dirty=Null
  430. glCheck()
  431. Return _glTexture
  432. End
  433. Protected
  434. #rem monkeydoc @hidden
  435. #end
  436. Method OnDiscard() Override
  437. If _cubeMap return
  438. If _glSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
  439. _discarded=True
  440. _managed=Null
  441. _glTexture=0
  442. _glSeq=0
  443. End
  444. #rem monkeydoc @hidden
  445. #end
  446. Method OnFinalize() Override
  447. If _cubeMap return
  448. If _glSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
  449. End
  450. Private
  451. Enum Dirty
  452. TexParams= 1
  453. TexImage= 2
  454. Mipmaps= 4
  455. All= 7
  456. End
  457. 'Global _boundSeq:Int
  458. 'Global _bound:=New GLuint[8]
  459. Field _size:Vec2i
  460. Field _format:PixelFormat
  461. Field _flags:TextureFlags
  462. Field _managed:Pixmap
  463. Field _cubeMap:Texture
  464. Field _cubeFaces:Texture[]
  465. Field _glTarget:GLenum
  466. Field _discarded:Bool
  467. Field _retroMode:Bool
  468. Field _dirty:Dirty
  469. Field _glSeq:Int
  470. Field _glTexture:GLuint
  471. Method Init( width:Int,height:Int,format:PixelFormat,flags:TextureFlags,managed:Pixmap )
  472. If flags & TextureFlags.Cubemap Assert( width=height,"Cubemaps must be square" )
  473. _size=New Vec2i( width,height )
  474. _format=format
  475. _flags=flags
  476. _glTarget=_flags & TextureFlags.Cubemap ? GL_TEXTURE_CUBE_MAP Else GL_TEXTURE_2D
  477. #If Not __DESKTOP_TARGET__
  478. If Not IsPow2( _size.x,_size.y ) _flags&=~TextureFlags.Mipmap
  479. #Endif
  480. If _flags & TextureFlags.Cubemap
  481. _cubeFaces=New Texture[6]
  482. For Local i:=0 Until 6
  483. Local face:=New Texture( _size.x,_size.y,_format,_flags & ~TextureFlags.Cubemap )
  484. face._glTarget=GL_TEXTURE_CUBE_MAP_POSITIVE_X+i
  485. face._cubeMap=Self
  486. _cubeFaces[i]=face
  487. Next
  488. Return
  489. Endif
  490. If Not (_flags & TextureFlags.Dynamic)
  491. If Not managed managed=New Pixmap( width,height,format )
  492. _managed=managed
  493. Endif
  494. End
  495. End
  496. Class ResourceManager Extension
  497. Method OpenTexture:Texture( path:String,flags:TextureFlags=Null )
  498. Local slug:="Texture:name="+StripDir( StripExt( path ) )+"&flags="+Int( flags )
  499. Local texture:=Cast<Texture>( OpenResource( slug ) )
  500. If texture Return texture
  501. Local pixmap:=OpenPixmap( path,Null,True )
  502. If Not pixmap Return Null
  503. texture=New Texture( pixmap,flags )
  504. AddResource( slug,texture )
  505. Return texture
  506. End
  507. End