Przeglądaj źródła

Updated several mojo classes to use extend Resource.

Mark Sibly 9 lat temu
rodzic
commit
7e2ad54893

+ 3 - 5
modules/mojo/app/app.monkey2

@@ -27,11 +27,9 @@ End
 
 #rem monkeydoc The AppInstance class.
 
-The AppInstance class is mainly reponsible for running the app 'event loop', but also provides several utility functions for managing
-the application.
+The AppInstance class is mainly reponsible for running the app 'event loop', but also provides several utility functions for managing the application.
 
-A global instance of the AppInstance class is stored in the [[App]] global variable, so you can use any member of the AppInstance simply
-by prefixing it with 'App.', eg: App.MilliSecs
+A global instance of the AppInstance class is stored in the [[App]] global variable, so you can use any member of the AppInstance simply by prefixing it with 'App.', eg: App.MilliSecs
 
 #end
 Class AppInstance
@@ -159,7 +157,7 @@ Class AppInstance
 		
 		SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )
 #Endif
-		_defaultFont=Font.Open( "asset::fonts/DejaVuSans.ttf",16 )
+		_defaultFont=Font.Open( "DejaVuSans",16 )
 		
 		_theme=Theme.Load( GetConfig( "initialTheme","asset::themes/default.json" ) )
 	End

+ 17 - 18
modules/mojo/graphics/font.monkey2

@@ -30,7 +30,7 @@ The glyph struct is used to store the location, size and advance for individual
 All character image data for a font must A font must occupy a single image, 
 
 #end
-Class Font
+Class Font Extends std.resource.Resource
 
 	#rem monkeydoc Creates a new font.
 
@@ -102,42 +102,41 @@ Class Font
 		Return w
 	End
 	
-	#rem monkeydoc @hidden
+	'Make this ALWAYS work!	
+	#rem monkeydoc Loads a font from a ttf file.
 	#end
-	Function Open:Font( path:String,height:Float )
+	Function Load:Font( path:String,height:Float,shader:Shader=Null )
 	
-		Local tag:=RealPath( path )+":"+height
+		If Not shader shader=Shader.GetShader( "font" )
 		
-		Local font:=_openFonts[tag]
-		If Not font
-			font=Load( path,height )
-			_openFonts[tag]=font
-		Endif
+		Local font:=fontloader.LoadFont( path,height,shader )
+		If Not font And Not ExtractRootDir( path ) font=fontloader.LoadFont( "font::"+path,height,shader )
 		
 		Return font
 	End
 
-	'Make this ALWAYS work!	
-	#rem monkeydoc Loads a font from a ttf file.
+	#rem monkeydoc @hidden
 	#end
-	Function Load:Font( path:String,height:Float,shader:Shader=Null )
+	Function Open:Font( path:String,height:Float,shader:Shader=Null )
 	
-		If Not shader shader=Shader.GetShader( "font" )
+		path=RealPath( path )
+		
+		Local slug:="Font:path="+path+"&height="+height+"&shader="+(shader ? shader.Name Else "")
+		
+		Local font:=Cast<Font>( OpenResource( slug ) )
+		If font Return font
 		
-		Local font:=mojo.graphics.fontloader.LoadFont( path,height,shader )
+		font=Load( path,height )
 		
+		AddResource( slug,font )
 		Return font
 	End
 
 	Private
 	
-	Private
-	
 	Field _image:Image
 	Field _height:Float
 	Field _firstChar:Int
 	Field _glyphs:Glyph[]
-	
-	Global _openFonts:=New StringMap<Font>
 
 End

+ 65 - 29
modules/mojo/graphics/fontloader.monkey2

@@ -15,13 +15,19 @@ Public
 #end
 Function LoadFont:Font( path:String,fheight:Float,shader:Shader )
 
+	Local ext:=ExtractExt( path )
+	If Not ext
+		Local font:=LoadFont( path+".otf",fheight,shader )
+		If Not font font=LoadFont( path+".ttf",fheight,shader )
+		Return font
+	Endif
+
 	If Not FreeType And FT_Init_FreeType( Varptr FreeType ) Return Null
 	
 	Local data:=DataBuffer.Load( path )
 	If Not data Return Null
 	
 	Local face:FT_Face
-	
 	If FT_New_Memory_Face( FreeType,data.Data,data.Length,0,Varptr face ) 
 		data.Discard()
 		Return Null
@@ -48,61 +54,91 @@ Function LoadFont:Font( path:String,fheight:Float,shader:Shader )
 '	Print face->size->metrics.descender/64.0
 
 	Local firstChar:=32,numChars:=96
-
 	Local glyphs:=New Glyph[numChars]
-	Local pixmap:=New Pixmap( 512,512,PixelFormat.A8 )
-	pixmap.Clear( Color.None )
-	
+
 	Local slot:=face->glyph
 	
-	Local x:=0,y:=0,h:=0
+	'Measure atlas first
+	'
+	'Would really rather not render glyphs here, but can't see how...
+	'
+	Local tx:=0,ty:=0,texw:=0,texh:=0,maxh:=0
 	
+	Const MaxTexWidth:=64'1024
+
 	For Local i:=0 Until numChars
 
-		'What to do?!?
-		'	
-'		If FT_Load_Char( face,firstChar+i,FT_LOAD_RENDER )
-'		If FT_Load_Char( face,firstChar+i,FT_LOAD_RENDER|FT_LOAD_TARGET_LIGHT )
-'		If FT_Load_Char( face,firstChar+i,FT_LOAD_RENDER|FT_LOAD_NO_HINTING )
 		If FT_Load_Char( face,firstChar+i,FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT )
 			Continue
 		Endif
+
+		Local gw:=Int( slot->bitmap.width )
+		Local gh:=Int( slot->bitmap.rows )
 		
-		#rem
-		If FT_Render_Glyph( slot,FT_RENDER_MODE_NORMAL )
-			Continue
+		If tx+gw+1>MaxTexWidth
+			texw=Max( texw,tx )
+			texh+=maxh
+			maxh=0
+			tx=0
 		Endif
-		#end
 		
-		Local gw:=slot->bitmap.width
-		Local gh:=slot->bitmap.rows
+		maxh=Max( maxh,gh+1 )
+		tx+=gw+1
 		
-		If x+gw+1>pixmap.Width
-			y+=h
-			h=0
-			x=0
+	Next
+	
+	texw=Max( texw,tx )
+	If tx texh+=maxh
+	
+	'round up texw, texh to ^2 in case we're mipmapping on mobile/webgl.
+	texw=1 Shl Int( Ceil( Log2( texw ) ) )
+	texh=1 Shl Int( Ceil( Log2( texh ) ) )
+	
+	Print "path="+path+", height="+fheight+", texw="+texw+", texh="+texh
+	
+	Local pixmap:=New Pixmap( texw,texh,PixelFormat.A8 )
+	pixmap.Clear( Color.None )
+	
+	tx=0;ty=0;maxh=0
+	
+	For Local i:=0 Until numChars
+
+		If FT_Load_Char( face,firstChar+i,FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT )
+			Continue
 		Endif
 		
+		Local gw:=Int( slot->bitmap.width )
+		Local gh:=Int( slot->bitmap.rows )
+
 		Local tmp:=New Pixmap( gw,gh,PixelFormat.A8,slot->bitmap.buffer,slot->bitmap.pitch )
 		
-		pixmap.Paste( tmp,x,y )
+		If tx+gw+1>pixmap.Width
+			ty+=maxh
+			maxh=0
+			tx=0
+		Endif
+		
+		pixmap.Paste( tmp,tx,ty )
 		
-		glyphs[i]=New Glyph( New Recti( x,y,x+gw,y+gh ),New Vec2f( slot->bitmap_left,ascent-slot->bitmap_top ),slot->advance.x Shr 6 )
+		glyphs[i]=New Glyph( New Recti( tx,ty,tx+gw,ty+gh ),New Vec2f( slot->bitmap_left,ascent-slot->bitmap_top ),slot->advance.x Shr 6 )
 
-		h=Max( Int(h),Int(gh)+1 )
-		x+=gw+1
+		maxh=Max( maxh,gh+1 )
+		tx+=gw+1
+		
 	Next
 	
 	FT_Done_Face( face )
 	
 	data.Discard()
 	
-	Local texture:=New Texture( pixmap,Null )
-	
-	Local image:=New Image( texture,shader )
+	Local image:=New Image( pixmap,Null,shader )
 	
 	Local font:=New Font( image,height,firstChar,glyphs )
 	
+	font.OnDiscarded+=Lambda()
+		image.Discard()
+		pixmap.Discard()
+	End
+	
 	Return font
-
 End

+ 76 - 62
modules/mojo/graphics/image.monkey2

@@ -8,12 +8,8 @@ An image is a rectangular array of pixels that can be drawn using one of the [[C
 You can load an image from a file using one of the [[Load]], [[LoadBump]] or [[LoadLight]] functions.
 
 #end
-Class Image
+Class Image Extends std.resource.Resource
 
-	#rem monkeydoc Invoked after image has ben discarded.
-	#end
-	Field OnDiscarded:Void()
-	
 	#rem monkeydoc Creates a new Image.
 	
 	New( pixmap,... ) Creates an image from an existing pixmap.
@@ -108,6 +104,7 @@ Class Image
 	#rem monkeydoc @hidden
 	#end
 	Method New( texture:Texture,rect:Recti,shader:Shader=Null )
+
 		Init( texture,rect,shader )
 	End
 	
@@ -323,89 +320,91 @@ Class Image
 		Return _textures[index]
 	End
 	
-	#rem monkeydoc Discards the image.
-	
-	Discards the image and releases any resources held by the image.
-	
-	#end
-	Method Discard()
-		If _discarded Return
-		_discarded=True
-		OnDiscarded()
-	End
-	
 	#rem monkeydoc Loads an image from file.
 	#end
 	Function Load:Image( path:String,shader:Shader=Null )
 	
+		Local pixmap:=Pixmap.Load( path,Null,True )
+		If Not pixmap Return Null
+
 		If Not shader shader=mojo.graphics.Shader.GetShader( "sprite" )
+		
+		Local image:=New Image( pixmap,Null,shader )
+			
+		image.OnDiscarded+=Lambda()
+			pixmap.Discard()
+		End
+		
+		Return image
+	End
+	
+	#rem monkeydoc @hidden experimental!
+	#end	
+	Function Open:Image( path:String,shader:Shader=Null )
 	
-		Local texture:=mojo.graphics.Texture.Load( path,Null )
-		If Not texture Return Null
+		path=RealPath( path )
 		
-		Return New Image( texture,shader )
+		Local slug:="Image:path="+path+"&shader="+(shader ? shader.Name Else "null")
+		
+		Local image:=Cast<Image>( OpenResource( slug ) )
+		If image Return image
+		
+		image=Load( path,shader )
+
+		AddResource( slug,image )
+		Return image
 	End
 	
 	#rem monkeydoc Loads a bump image from file(s).
 	
 	`diffuse`, `normal` and `specular` are filepaths of the diffuse, normal and specular image files respectively.
 	
-	`specular` can be null, in which case `specularScale` is used for the specular component. Otherwise, `specularScale` is used to modulate the specular components of the 
-	specular texture.
+	`specular` can be null, in which case `specularScale` is used for the specular component. Otherwise, `specularScale` is used to modulate the red component of the specular texture.
 	
 	#end
 	Function LoadBump:Image( diffuse:String,normal:String,specular:String,specularScale:Float=1,flipNormalY:Bool=True,shader:Shader=Null )
 	
-		If Not shader shader=mojo.graphics.Shader.GetShader( "bump" )
-
-		Local pdiff:=Pixmap.Load( diffuse )
-		Local pnorm:=Pixmap.Load( normal )
-		Local pspec:=Pixmap.Load( specular )
+		Local texture1:=graphics.Texture.LoadNormal( normal,Null,specular,specularScale,flipNormalY )
+		If Not texture1 Return Null
 		
-		If pdiff
-			pdiff.PremultiplyAlpha()
-		Else
-			pdiff=New Pixmap( pnorm.Width,pnorm.Height,PixelFormat.I8 )
-			pdiff.Clear( std.graphics.Color.White )
-		Endif
-		
-		Local yxor:=flipNormalY ? $ff00 Else 0
+		Local texture0:=graphics.Texture.Load( diffuse,Null )
 		
-		If pspec And pspec.Width=pnorm.Width And pspec.Height=pnorm.Height
-			For Local y:=0 Until pnorm.Height
-				For Local x:=0 Until pnorm.Width
-					Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
-					Local s:=(pspec.GetPixelARGB( x,y ) Shr 16) & $ff
-					n=n & $ffffff00 | Clamp( Int( specularScale * s ),1,255 )
-					pnorm.SetPixelARGB( x,y,n )
-				Next
-			Next
-			pspec.Discard()
-		Else
-			Local g:=Clamp( Int( specularScale * 255.0 ),1,255 )
-			For Local y:=0 Until pnorm.Height
-				For Local x:=0 Until pnorm.Width
-					Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
-					n=n & $ffffff00 | g
-					pnorm.SetPixelARGB( x,y,n )
-				Next
-			Next
-			If pspec pspec.Discard()
+		If Not texture0
+			Local pdiff:=New Pixmap( texture1.Width,texture1.Height,PixelFormat.I8 )
+			pdiff.Clear( std.graphics.Color.White )
+			texture0=New graphics.Texture( pdiff,Null )
 		Endif
 		
-		Local texture0:=New Texture( pdiff,Null )
-		Local texture1:=New Texture( pnorm,Null )
+		If Not shader shader=graphics.Shader.GetShader( "bump" )
 		
 		Local image:=New Image( texture0,texture0.Rect,shader )
 		image.SetTexture( 1,texture1 )
 		
 		image.OnDiscarded+=Lambda()
-			texture0.Discard()
-			texture1.Discard()
-			pdiff.Discard()
-			pnorm.Discard()
+			If texture0 texture0.Discard()
+			If texture1 texture1.Discard()
 		End
+		
+		Return image
+	End
+
+	#rem monkeydoc @hidden experimental!
+	#end	
+	Function OpenBump:Image( diffuse:String,normal:String,specular:String,specularScale:Float=1,flipNormalY:Bool=True,shader:Shader=Null )
 	
+		diffuse=RealPath( diffuse )
+		normal=RealPath( normal )
+		specular=RealPath( specular )
+
+		Local slug:="BumpImage:diffuse="+diffuse+"&normal="+normal+"&specular="+specular
+		slug+="&specularScale="+specularScale+"&flipNormalY="+Int( flipNormalY )+"&shader="+(shader ? shader.Name Else "")
+		
+		Local image:=Cast<Image>( OpenResource( slug ) )
+		If image Return image
+		
+		image=LoadBump( diffuse,normal,specular,specularScale,flipNormalY,shader )
+
+		AddResource( slug,image )	
 		Return image
 	End
 	
@@ -481,13 +480,28 @@ Class Image
 		Return image
 	End
 
+	#rem monkeydoc @hidden experimental!
+	#end
+	Function OpenLight:Image( path:String,shader:Shader=Null )
+	
+		path=RealPath( path )
+		
+		Local slug:="Light:path="+path+"&shader="+(shader ? shader.Name Else Null)
+		
+		Local light:=Cast<Image>( OpenResource( path ) )
+		If light Return light
+		
+		light=Load( path,shader )
+		
+		AddResource( slug,light )
+		Return light
+	End
+
 	Private
 	
 	Field _shader:Shader
 	Field _material:UniformBlock
 
-	Field _discarded:Bool
-	
 	Field _textures:=New Texture[4]
 	Field _blendMode:BlendMode
 	Field _textureFilter:TextureFilter

+ 12 - 2
modules/mojo/graphics/shader.monkey2

@@ -162,13 +162,22 @@ Class Shader
 	
 		Assert( Not _shaders.Contains( name ),"Shader with name '"+name+"' already exists" )
 		
-		_shaders[name]=Self
+		_name=name
 	
 		_source=source
 		
+		_shaders[name]=Self
+		
 		EnumPasses()
 	End
 	
+	#rem monkeydoc The shader name.
+	#end
+	Property Name:String()
+	
+		Return _name
+	End
+	
 	#rem monkeydoc The shader source code.
 	#end
 	Property Source:String()
@@ -227,7 +236,8 @@ Class Shader
 	Private
 	
 	Global _shaders:=New StringMap<Shader>
-	
+
+	Field _name:String	
 	Field _source:String
 	Field _rpasses:Int[]
 	Field _rpassMask:Int

+ 94 - 22
modules/mojo/graphics/texture.monkey2

@@ -41,11 +41,7 @@ End
 
 #rem monkeydoc @hidden
 #end
-Class Texture
-
-	#rem monkeydoc @hidden
-	#end
-	Field OnDiscarded:Void()
+Class Texture Extends std.resource.Resource
 
 	Method New( pixmap:Pixmap,flags:TextureFlags )
 	
@@ -69,7 +65,7 @@ Class Texture
 	Method New( width:Int,height:Int,format:PixelFormat,flags:TextureFlags )
 	
 #If Not __DESKTOP_TARGET__
-		Local tw:=Log2( Width ),th:=Log2( Height )
+		Local tw:=Log2( width ),th:=Log2( height )
 		If tw<>Round( tw ) Or th<>Round( th ) flags|=TextureFlags.DisableMipmap
 #Endif
 		_rect=New Recti( 0,0,width,height )
@@ -108,16 +104,6 @@ Class Texture
 		Return _format
 	End
 	
-	Method Discard()
-		If _discarded Return
-		If _texSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
-		If _fbSeq=glGraphicsSeq glDeleteFramebuffers( 1,Varptr _glFramebuffer )
-		_glTexture=0
-		_glFramebuffer=0
-		_discarded=True
-		OnDiscarded()
-	End
-	
 	Method PastePixmap( pixmap:Pixmap,x:Int,y:Int )
 	
 		If _managed
@@ -146,14 +132,12 @@ Class Texture
 		Endif
 	
 	End
-	
+
 	Function Load:Texture( path:String,flags:TextureFlags )
-	
-		Local pixmap:=Pixmap.Load( path )
+
+		Local pixmap:=Pixmap.Load( path,,True )
 		If Not pixmap Return Null
 		
-		pixmap.PremultiplyAlpha()
-		
 		Local texture:=New Texture( pixmap,flags )
 		
 		texture.OnDiscarded+=Lambda()
@@ -162,6 +146,82 @@ Class Texture
 		
 		Return texture
 	End
+	
+	#rem monkeydoc @hidden experimental!
+	#end
+	Function Open:Texture( path:String,textureFlags:TextureFlags )
+	
+		path=RealPath( path )
+		
+		Local slug:="Texture:path="+path+"&flags="+Int( textureFlags )
+		
+		Local texture:=Cast<Texture>( OpenResource( slug ) )
+		If texture Return texture
+		
+		texture=Load( path,textureFlags )
+		
+		AddResource( slug,texture )
+		Return texture
+	End
+	
+	Function LoadNormal:Texture( path:String,textureFlags:TextureFlags,specular:String,specularScale:Float=1,flipNormalY:Bool=True )
+
+		path=RealPath( path )
+		specular=specular ? RealPath( specular ) Else ""
+
+		Local pnorm:=Pixmap.Load( path,,True )
+		If Not pnorm Return Null
+		
+		Local pspec:=Pixmap.Load( specular )
+		
+		Local yxor:=flipNormalY ? $ff00 Else 0
+			
+		If pspec And pspec.Width=pnorm.Width And pspec.Height=pnorm.Height
+			For Local y:=0 Until pnorm.Height
+				For Local x:=0 Until pnorm.Width
+					Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
+					Local s:=(pspec.GetPixelARGB( x,y ) Shr 16) & $ff
+					n=n & $ffffff00 | Clamp( Int( specularScale * s ),1,255 )
+					pnorm.SetPixelARGB( x,y,n )
+				Next
+			Next
+		Else
+			Local g:=Clamp( Int( specularScale * 255.0 ),1,255 )
+			For Local y:=0 Until pnorm.Height
+				For Local x:=0 Until pnorm.Width
+					Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
+					n=n & $ffffff00 | g
+					pnorm.SetPixelARGB( x,y,n )
+				Next
+			Next
+		Endif
+			
+		If pspec pspec.Discard()
+			
+		Local texture:=New Texture( pnorm,Null )
+		Return texture
+		
+	End
+	
+	#rem monkeydoc @hidden experimental!
+	#end
+	Function OpenNormal:Texture( path:String,textureFlags:TextureFlags,specular:String,specularScale:Float=1,flipNormalY:Bool=True )
+	
+		path=RealPath( path )
+		
+		specular=specular ? RealPath( specular ) Else ""
+		
+		Local slug:="NormalTexture:path="+path+"?flags="+Int( textureFlags )
+		slug+="?specular="+specular+"&specularScale="+specularScale+"&flipNormalY="+Int( flipNormalY )
+		
+		Local texture:=Cast<Texture>( OpenResource( slug ) )
+		If texture Return texture
+		
+		texture=LoadNormal( path,textureFlags,specular,specularScale,flipNormalY )
+		
+		AddResource( slug,texture )
+		Return texture
+	End
 
 	Function ColorTexture:Texture( color:Color )
 		Local texture:=_colorTextures[color]
@@ -280,7 +340,7 @@ Class Texture
 	#rem monkeydoc @hidden
 	#end	
 	Property GLFramebuffer:GLuint()
-		DebugAssert( Not _discarded,"texture has been discarded" )
+		If _discarded Return 0
 	
 		If _fbSeq=glGraphicsSeq Return _glFramebuffer
 		
@@ -341,6 +401,18 @@ Class Texture
 		_dirty|=Dirty.Mipmaps
 	End
 	
+	Protected
+
+	#rem monkeydoc @hidden
+	#end	
+	Method OnDiscard() Override
+		If _texSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
+		If _fbSeq=glGraphicsSeq glDeleteFramebuffers( 1,Varptr _glFramebuffer )
+		_glTexture=0
+		_glFramebuffer=0
+		_discarded=True
+	End
+	
 	Private
 	
 	Enum Dirty