IlluminatedSprite - Lighting effects with sprites only

Share your advanced PureBasic knowledge/code with the community.
User avatar
STARGÅTE
Addict
Addict
Posts: 2228
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

IlluminatedSprite - Lighting effects with sprites only

Post by STARGÅTE »

With this small module I want to show you that it is possible to create believable lighting for sprite using native PureBasic sprite commands without the need of 3D commands. All you need is the image as a diffuse map (i.e. the normal color representation) and the image as a normal map (i.e. the representation of the surface slopes).
With the module you can then create illuminated sprites (IlluminatedSprite) and display them on the screen like normal sprites, with the benefit that they can be illuminated using lights placed in the scene (SpriteLight) (examples in the next post):
Image

Internally, the module create a set of images for different lighting directions from the Diffuse Map and the Normal Map and then mix them in real time when displaying:
Image + Image = Image
When DisplayIlluminatedSprite() is executed, one lighting version is mixed from all lights and is displayed. More lights (i.e. many lights) do not mean more sprites that need to be displayed. A maximum of 30 DisplaySprite commands are executed per DisplayIlluminatedSprite command.
Of course, this is anything but efficient and accurate compared to real solutions with shaders, but that is not the topic here. It is just a simple "trick" and is intended to show what is possible with "pure"-Basic without a 3D engine. And perhaps, a nice add-on for a small game.

Code: Select all


;- Illuminated Sprites

DeclareModule IlluminatedSprites
	
	Enumeration
		#SpriteLight_Ambient      ; An ambient light source that illuminates the whole sprite angle independent.
		#SpriteLight_Directional  ; A directional light source that illuminates the sprite from the specified direction.
		#SpriteLight_Point        ; A point light source that illuminates the sprite from the specified position and range.
	EndEnumeration
	
	; Procedure for the illuminated sprites:	
	Declare.i CreateIlluminatedSprite(Sprite_DiffuseMap.i, Sprite_NormalMap.i, WeightingZ.f = 1.0)   ; Creates an IlluminatedSprite from a diffuse and normal map.
	Declare.i DisplayIlluminatedSprite(*IlluminatedSprite, X.f, Y.f, Z.f=0.0)                        ; Displays the IlluminatedSprite at the given position.
	Declare.i RotateIlluminatedSprite(*IlluminatedSprite, Angle.f)                                   ; Rotates the IlluminatedSprite to the given angle in degree.
	Declare.i ZoomIlluminatedSprite(*IlluminatedSprite, Factor.f)                                    ; Zooms the IlluminatedSprite by the given factor relative to the original size.
	Declare.i FreeIlluminatedSprite(*IlluminatedSprite)                                              ; Deletes the given IlluminatedSprite
	
	; Procedure for the light sources:	
	Declare.i CreateSpriteLight(Type.i, Color.q, Intensity.f=1.0, X.f=0, Y.f=0, Z.f=0, Range.f=1000.0)  ; Create a SpriteLight with the given color and parameters:
	                                                                                                    ; - #SpriteLight_Ambient: X, Y, Z and Range have no meaning.
	                                                                                                    ; - #SpriteLight_Directional: X, Y, and Z are the direction, Range has no meening.
	                                                                                                    ; - #SpriteLight_Point: X, Y, and Z are the position, Range is the max distance of illumination.
	Declare.i FreeSpriteLight(*SpriteLight)                       ; Deletes the given SpriteLight
	Declare.i SpriteLightIntensity(*SpriteLight, Intensity.f)     ; Changes the intensity of a light source
	Declare.i SpriteLightRange(*SpriteLight, Range.f)             ; Changes the range of a point light source
	Declare.i SpriteLightDirection(*SpriteLight, X.f, Y.f, Z.f)   ; Changes the direction of a directional light source
	Declare.i SpriteLightPosition(*SpriteLight, X.f, Y.f, Z.f=0)  ; Changes the position of a point light source
	
EndDeclareModule


Module IlluminatedSprites
	
	EnableExplicit
	
	#Inverse255 = 1.0/255.0
	#TwoInverse255 = 2.0/255.0
	#SquaredInverse255 = 1.0/(255.0*255.0)
	#LightDirections = 10
	
	Structure Color
		Channel.f[0]
		R.f
		G.f
		B.f
		A.f
	EndStructure
	
	Structure Vector
		X.f
		Y.f
		Z.f
	EndStructure
	
	Structure IlluminatedSprite
		Sprite_Diffuse.i
		Sprite_Color.i
		Rotation.f
		Zoom.f
		Width.i
		Height.i
	EndStructure
	
	Global NewList IlluminatedSprite.IlluminatedSprite()
	
	Structure SpriteLight
		Type.i
		Color.Color
		StructureUnion
			Position.Vector
			Direction.Vector
		EndStructureUnion
		Intensity.f
		Range.f
	EndStructure
	
	Global NewList SpriteLight.SpriteLight()
	
	
	;- Some helper vector math functions
	
	Procedure.f Vector_Length(*Vector.Vector)
		ProcedureReturn Sqr(*Vector\X**Vector\X+*Vector\Y**Vector\Y+*Vector\Z**Vector\Z)
	EndProcedure
	
	Procedure.f Vector_Dot(*Vector1.Vector, *Vector2.Vector)
		ProcedureReturn *Vector1\X * *Vector2\X + *Vector1\Y * *Vector2\Y + *Vector1\Z * *Vector2\Z
	EndProcedure
	
	Procedure.i Vector_Normalize(*Vector.Vector)
		Protected Length.f = Vector_Length(*Vector)
		*Vector\X / Length  :  *Vector\Y / Length  :  *Vector\Z / Length
		ProcedureReturn *Vector
	EndProcedure
	
	Procedure.i Vector_Rotate(*Vector.Vector, Angle.f)
		Protected OldVector.Vector
		OldVector\X = *Vector\X  :  OldVector\Y = *Vector\Y
		*Vector\X = Cos(Angle) * OldVector\X - Sin(Angle) * OldVector\Y
		*Vector\Y = Sin(Angle) * OldVector\X + Cos(Angle) * OldVector\Y
		ProcedureReturn *Vector
	EndProcedure
	
	;- Procedures for the lights
	
	Procedure.i CreateSpriteLight(Type.i, Color.q, Intensity.f=1.0, X.f=0, Y.f=0, Z.f=0, Range.f=1000.0)
		
		Select Type
			Case #SpriteLight_Ambient
				AddElement(SpriteLight())
				SpriteLight()\Type      = #SpriteLight_Ambient
			Case #SpriteLight_Directional
				AddElement(SpriteLight())
				SpriteLight()\Type        = #SpriteLight_Directional
				SpriteLight()\Direction\X = X
				SpriteLight()\Direction\Y = Y
				SpriteLight()\Direction\Z = Z
				Vector_Normalize(SpriteLight()\Direction)
			Case #SpriteLight_Point
				AddElement(SpriteLight())
				SpriteLight()\Type  = #SpriteLight_Point
				SpriteLight()\Position\X = X
				SpriteLight()\Position\Y = Y
				SpriteLight()\Position\Z = Z
			Default
				ProcedureReturn #False
		EndSelect
		
		With SpriteLight()
			\Color\R   = #Inverse255 * Red(Color)
			\Color\G   = #Inverse255 * Green(Color)
			\Color\B   = #Inverse255 * Blue(Color)
			\Range     = Range
			\Intensity = Intensity
		EndWith
				
		ProcedureReturn SpriteLight()
		
	EndProcedure
	
	Procedure   FreeSpriteLight(*SpriteLight.SpriteLight)
		
		ChangeCurrentElement(SpriteLight(), *SpriteLight)
		DeleteElement(SpriteLight())
		
	EndProcedure
	
	Procedure.i SpriteLightPosition(*SpriteLight.SpriteLight, X.f, Y.f, Z.f=0.0)
		
		*SpriteLight\Position\X = X
		*SpriteLight\Position\Y = Y
		*SpriteLight\Position\Z = Z
		
	EndProcedure
	
	Procedure.i SpriteLightDirection(*SpriteLight.SpriteLight, X.f, Y.f, Z.f)
		
		*SpriteLight\Direction\X = X
		*SpriteLight\Direction\Y = Y
		*SpriteLight\Direction\Z = Z
		Vector_Normalize(*SpriteLight\Direction)
		
	EndProcedure
	
	Procedure.i SpriteLightIntensity(*SpriteLight.SpriteLight, Intensity.f)
		
		*SpriteLight\Intensity = Intensity
		
	EndProcedure
	
	Procedure.i SpriteLightRange(*SpriteLight.SpriteLight, Range.f)
		
		*SpriteLight\Range = Range
		
	EndProcedure
	
	
	;- Procedures for the illuminated sprites
	
	Procedure.i CreateIlluminatedSprite(Sprite_DiffuseMap.i, Sprite_NormalMap.i, WeightingZ.f = 1.0)
		
		Protected Dim Color.Color(0, 0), *Color.Color
		Protected Dim Normal.Vector(0, 0), *Normal.Vector
		Protected *IlluminationDirection.Vector
		Protected Sprite_ColorMap.i, Color.l
		Protected X.i, Y.i, Width.i, Height.i
		Protected Dot.f, DotXY.f, DotWeighted.f
		Protected Temp.Vector
		Protected Part.i
	
		If Not IsSprite(Sprite_DiffuseMap) Or Not IsSprite(Sprite_NormalMap)
			ProcedureReturn #False
		EndIf
		
		Width  = SpriteWidth(Sprite_DiffuseMap)
		Height = SpriteHeight(Sprite_NormalMap)
		If Width <> SpriteWidth(Sprite_NormalMap) Or Height <> SpriteHeight(Sprite_NormalMap)
			ProcedureReturn #False
		EndIf
		
		Dim Color(Width-1, Height-1)
		Dim Normal(Width-1, Height-1)
		
		; Import color data from the diffuse map.
		If StartDrawing(SpriteOutput(Sprite_DiffuseMap))
			DrawingMode(#PB_2DDrawing_AllChannels)
			For Y = Height-1 To 0 Step -1
				For X = Width-1 To 0 Step -1
					Color    = Point(X, Y)
					*Color   = Color(X, Y)
					*Color\R = #SquaredInverse255 * Alpha(Color) * Red(Color)
					*Color\G = #SquaredInverse255 * Alpha(Color) * Green(Color)
					*Color\B = #SquaredInverse255 * Alpha(Color) * Blue(Color)
				Next
			Next
			StopDrawing()
		EndIf
		
		; Import normal vector data from the normal map.
		If StartDrawing(SpriteOutput(Sprite_NormalMap))
			DrawingMode(#PB_2DDrawing_AllChannels)
			For Y = Height-1 To 0 Step -1
				For X = Width-1 To 0 Step -1
					Color     = Point(X, Y)
					*Normal   = Normal(X, Y)
					*Normal\X = #TwoInverse255 * Red(Color) - 1.0
					*Normal\Y = -(#TwoInverse255 * Green(Color) - 1.0)
					*Normal\Z = (#TwoInverse255 * Blue(Color) - 1.0) * WeightingZ
					Vector_Normalize(*Normal)
				Next
			Next
			StopDrawing()
		EndIf
		
		; Create the special illuminated Sprite, a grid of three colors,
		;   each having the ambient light version and nine light directions.
		*IlluminationDirection = ?IlluminationDirections
		Sprite_ColorMap = CreateSprite(#PB_Any, #LightDirections*Width, 3*Height)
		If StartDrawing(SpriteOutput(Sprite_ColorMap))
			For Part = 0 To #LightDirections-1
				For Y = Height-1 To 0 Step -1
					For X = Width-1 To 0 Step -1
						*Normal = Normal(X, Y)
						*Color  = Color(X, Y)
						If Part > 0
							Dot = Vector_Dot(*Normal, *IlluminationDirection)
							Temp\X = *Normal\X
							Temp\Y = *Normal\Y
							Vector_Normalize(Temp)
							DotXY = Vector_Dot(Temp, *IlluminationDirection)
							If Part = 1
								DotWeighted = Dot 
							Else
								DotWeighted = DotXY * Pow(Dot,2.01)
							EndIf
							If Dot > 0.0 And DotWeighted > 0
								Plot(X+Width*Part, Y+Height*0, RGB(*Color\R*DotWeighted*255, 0, 0))
								Plot(X+Width*Part, Y+Height*1, RGB(0, *Color\G*DotWeighted*255, 0))
								Plot(X+Width*Part, Y+Height*2, RGB(0, 0, *Color\B*DotWeighted*255))
							EndIf
						Else
							Plot(X, Y+Height*0, RGB(*Color\R*255, 0, 0))
							Plot(X, Y+Height*1, RGB(0, *Color\G*255, 0))
							Plot(X, Y+Height*2, RGB(0, 0, *Color\B*255))
						EndIf
					Next
				Next
				*IlluminationDirection + SizeOf(Vector)
			Next
			StopDrawing()
		EndIf
		
		AddElement(IlluminatedSprite())
		With IlluminatedSprite()
			\Sprite_Diffuse   = Sprite_DiffuseMap
			\Sprite_Color     = Sprite_ColorMap
			\Width            = Width
			\Height           = Height
			\Rotation         = 0
			\Zoom             = 1.0
		EndWith
		
		ProcedureReturn IlluminatedSprite()
		
	EndProcedure
	
	Procedure   FreeIlluminatedSprite(*IlluminatedSprite.IlluminatedSprite)
		
		FreeSprite(*IlluminatedSprite\Sprite_Color)
		ChangeCurrentElement(IlluminatedSprite(), *IlluminatedSprite)
		DeleteElement(IlluminatedSprite())
		
	EndProcedure
	
	Procedure.i DisplayColorChannels(*IlluminatedSprite.IlluminatedSprite, *Color.Color, Part.i, X.f, Y.f)
		
		Protected I.i
		
		With *IlluminatedSprite
			For I = 0 To 2
				If *Color\Channel[I] > 0
					ClipSprite(\Sprite_Color, \Width*Part, \Height*I, \Width, \Height)
					RotateSprite(\Sprite_Color, \Rotation, #PB_Absolute)
					ZoomSprite(\Sprite_Color, \Width*\Zoom, \Height*\Zoom)
					DisplayTransparentSprite(\Sprite_Color, X, Y, 255 * *Color\Channel[I])
					;If *Color\Channel[I] > 1 ; One more drawing if the intensity is brighter than 1.0, not used at the moment
					;	DisplayTransparentSprite(\Sprite_Color, X, Y, 255 * (*Color\Channel[I]-1))
					;EndIf
				EndIf
			Next
		EndWith
		
	EndProcedure
	
	Procedure.i RotateIlluminatedSprite(*IlluminatedSprite.IlluminatedSprite, Angle.f)
		
		*IlluminatedSprite\Rotation = Angle
		
	EndProcedure
	
	Procedure.i ZoomIlluminatedSprite(*IlluminatedSprite.IlluminatedSprite, Factor.f)
		
		*IlluminatedSprite\Zoom = Factor
		
	EndProcedure
	
	Procedure.i DisplayIlluminatedSprite(*IlluminatedSprite.IlluminatedSprite, X.f, Y.f, Z.f=0.0)
		
		Protected *IlluminationDirections.Vector
		Protected LightColor.Color
		Protected Part.i, Direction.Vector, Intensity.f, Distance.f, Weighting.f
		Protected View.Vector
		
		With *IlluminatedSprite
			
			RotateSprite(\Sprite_Diffuse, \Rotation, #PB_Absolute)
			ZoomSprite(\Sprite_Diffuse, \Width*\Zoom, \Height*\Zoom)
			DisplayTransparentSprite(\Sprite_Diffuse, X-\Zoom*\Width/2, Y-\Zoom*\Height/2, 255, 0)
			
			SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendOne)
			*IlluminationDirections = ?IlluminationDirections
			For Part = 0 To #LightDirections-1
				ResetStructure(@LightColor, Color)
				ForEach SpriteLight()
					Intensity = 0.0
					Select SpriteLight()\Type
						Case #SpriteLight_Ambient
							If Part = 0
								Intensity = SpriteLight()\Intensity
							EndIf
						Case #SpriteLight_Directional
							Direction = SpriteLight()\Direction
							Vector_Rotate(Direction, -Radian(\Rotation))
							If Part = 1
								Intensity = Vector_Dot(Direction, *IlluminationDirections) * SpriteLight()\Intensity
							Else
								Intensity = Vector_Dot(Direction, *IlluminationDirections) * SpriteLight()\Intensity * 0.7
							EndIf
						Case #SpriteLight_Point
							Direction\X = SpriteLight()\Position\X - X
							Direction\Y = SpriteLight()\Position\Y - Y
							Direction\Z = SpriteLight()\Position\Z - Z
							Distance = Vector_Length(Direction)
							If Distance < SpriteLight()\Range
								Vector_Normalize(Direction)
								Vector_Rotate(Direction, -Radian(\Rotation))
								If Part = 1
									Intensity = Vector_Dot(Direction, *IlluminationDirections) * SpriteLight()\Intensity * (1 - Distance/SpriteLight()\Range) 
								Else
									Intensity = Vector_Dot(Direction, *IlluminationDirections) * SpriteLight()\Intensity * (1 - Distance/SpriteLight()\Range) * 0.7
								EndIf
							EndIf
					EndSelect
					If Intensity > 0
						LightColor\R + Intensity * SpriteLight()\Color\R
						LightColor\G + Intensity * SpriteLight()\Color\G
						LightColor\B + Intensity * SpriteLight()\Color\B
					EndIf
				Next
				DisplayColorChannels(*IlluminatedSprite, LightColor, Part, X-\Zoom*\Width/2, Y-\Zoom*\Height/2)
				*IlluminationDirections + SizeOf(Vector)
			Next
			SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendInvertSourceAlpha)
			
		EndWith
		
	EndProcedure
	
	
	DataSection
		IlluminationDirections:
		Data.f  0.0,  0.0, 0.0  ; Ambient
		Data.f  0.0,  0.0, 1.0  ; Front
		Data.f  1.0,  0.0, 0.0  ; Right
		Data.f  0.0,  1.0, 0.0  ; Bottom
		Data.f -1.0,  0.0, 0.0  ; Left
		Data.f  0.0, -1.0, 0.0  ; Top
		Data.f  0.7,  0.7, 0.0  ; Right-Bottom
		Data.f -0.7,  0.7, 0.0  ; Left-Bottom
		Data.f -0.7, -0.7, 0.0  ; Left-Top
		Data.f  0.7, -0.7, 0.0  ; Right-Top
	EndDataSection
	
	
EndModule
Last edited by STARGÅTE on Thu Jan 09, 2025 2:10 pm, edited 1 time in total.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
STARGÅTE
Addict
Addict
Posts: 2228
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: IlluminatedSprite - Lighting effects with sprites only

Post by STARGÅTE »

Examples for the module:

Note: The respective example images are downloaded from the code itself and saved in temp directory.
______

Image

Code: Select all

UseModule IlluminatedSprites

Enumeration
	#Window
	#Font
	#Sprite_Moon_DiffuseMap
	#Sprite_Moon_NormalMap
	#Sprite_LightPoint
EndEnumeration

UsePNGImageDecoder()
UsePNGImageEncoder()

InitSprite()
	
LoadFont(#Font, "Arial", 12, #PB_Font_Bold)

OpenWindow(#Window, 0, 0, 900, 600, "Illuminated Sprites", #PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
OpenWindowedScreen(WindowID(#Window), 0, 0, WindowWidth(#Window), WindowHeight(#Window))

CreateSprite(#Sprite_LightPoint, 16, 16)
If StartDrawing(SpriteOutput(#Sprite_LightPoint))
	Circle(8,8,8,$FFFFFF)
	StopDrawing()
EndIf

If FileSize(GetTemporaryDirectory()+"moon_diffuse.png") = -1
	ReceiveHTTPFile("https://data.unionbytes.de/illuminatedsprite/moon_diffuse.png", GetTemporaryDirectory()+"moon_diffuse.png")
EndIf
If FileSize(GetTemporaryDirectory()+"moon_normal.png") = -1
	ReceiveHTTPFile("https://data.unionbytes.de/illuminatedsprite/moon_normal.png", GetTemporaryDirectory()+"moon_normal.png")
EndIf
LoadSprite(#Sprite_Moon_DiffuseMap, GetTemporaryDirectory()+"moon_diffuse.png", #PB_Sprite_AlphaBlending)
LoadSprite(#Sprite_Moon_NormalMap, GetTemporaryDirectory()+"moon_normal.png")

; Create an IlluminatedSprite from the two sprites of the moon with DiffuseMap and NormalMap
Define Moon = CreateIlluminatedSprite(#Sprite_Moon_DiffuseMap, #Sprite_Moon_NormalMap)

CreateSpriteLight(#SpriteLight_Ambient, $404040) ;- Base illumination for all illuminated Sprites
CreateSpriteLight(#SpriteLight_Directional, $C00000, 1.0, 1.0, -0.7, 0.0) ;- Directional illumination

Define Light_Red = CreateSpriteLight(#SpriteLight_Point, $0000FF) ;- Point-light illumination
Define Light_Green = CreateSpriteLight(#SpriteLight_Point, $00FF40, 1.0, 0, 0, 0, 500) ;- Point-light illumination with shorter range

Define Time.f

Repeat
	
	Repeat
		
		Select WindowEvent()
			Case #PB_Event_None
				Break
			Case #PB_Event_CloseWindow
				Break 2
		EndSelect
		
	ForEver
	
	Time = ElapsedMilliseconds()/2000
	
	ClearScreen($000000)
	
	SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendInvertSourceAlpha)
		
	RotateIlluminatedSprite(Moon, Time*20)
	DisplayIlluminatedSprite(Moon, ScreenWidth()/3, ScreenHeight()/2)
	
	ZoomSprite(#Sprite_Moon_DiffuseMap, 128, 128)
	RotateSprite(#Sprite_Moon_DiffuseMap, 0, #PB_Absolute)
	DisplayTransparentSprite(#Sprite_Moon_DiffuseMap, ScreenWidth()*0.8, ScreenHeight()*0.2)
	
	ZoomSprite(#Sprite_Moon_NormalMap, 128, 128)
	RotateSprite(#Sprite_Moon_NormalMap, 0, #PB_Absolute)
	DisplayTransparentSprite(#Sprite_Moon_NormalMap, ScreenWidth()*0.8, ScreenHeight()*0.6)
	
	If Light_Red
		SpriteLightPosition(Light_Red, (Cos(Time)+1)*ScreenWidth()/2, (Sin(Time*2.4)+1)*ScreenHeight()/2, 50)
		DisplayTransparentSprite(#Sprite_LightPoint, (Cos(Time)+1)*ScreenWidth()/2-8, (Sin(Time*2.4)+1)*ScreenHeight()/2-8, 255, $0000FF)
	EndIf
		
	If Light_Green
		If WindowMouseX(#Window) = -1 And  WindowMouseY(#Window) = -1
			SpriteLightIntensity(Light_Green, 0.0)
		Else
			SpriteLightIntensity(Light_Green, 1.0)
			SpriteLightPosition(Light_Green, WindowMouseX(#Window), WindowMouseY(#Window), 50)
			DisplayTransparentSprite(#Sprite_LightPoint, WindowMouseX(#Window)-8, WindowMouseY(#Window)-8, 255, $00FF00)
		EndIf
	EndIf
	
	If StartDrawing(ScreenOutput())
		DrawingMode(#PB_2DDrawing_Transparent)
		DrawingFont(FontID(#Font))
		DrawText(710, 70, "Sprite: Diffuse map")
		DrawText(710, 300, "Sprite: Normal map")
 		DrawText(230, 70, "IlluminatedSprite")
		StopDrawing()
	EndIf
	
	FlipBuffers()
	
ForEver

End
______

Image

Code: Select all

UseModule IlluminatedSprites

Enumeration
	#Window
	#Font
	#Sprite_Tower_DiffuseMap
	#Sprite_Tower_NormalMap
	#Sprite_LightPoint
EndEnumeration

UsePNGImageDecoder()

InitSprite()

LoadFont(#Font, "Arial", 12, #PB_Font_Bold)

OpenWindow(#Window, 0, 0, 800, 500, "Illuminated Sprites", #PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
OpenWindowedScreen(WindowID(#Window), 0, 0, WindowWidth(#Window), WindowHeight(#Window))

CreateSprite(#Sprite_LightPoint, 16, 16)
If StartDrawing(SpriteOutput(#Sprite_LightPoint))
	Circle(8,8,8,$FFFFFF)
	StopDrawing()
EndIf

	
If FileSize(GetTemporaryDirectory()+"castle_tower_diffuse.png") = -1
	ReceiveHTTPFile("https://data.unionbytes.de/illuminatedsprite/castle_tower_diffuse.png", GetTemporaryDirectory()+"castle_tower_diffuse.png")
EndIf
If FileSize(GetTemporaryDirectory()+"castle_tower_normal.png") = -1
	ReceiveHTTPFile("https://data.unionbytes.de/illuminatedsprite/castle_tower_normal.png", GetTemporaryDirectory()+"castle_tower_normal.png")
EndIf
LoadSprite(#Sprite_Tower_DiffuseMap, GetTemporaryDirectory()+"castle_tower_diffuse.png", #PB_Sprite_AlphaBlending)
LoadSprite(#Sprite_Tower_NormalMap, GetTemporaryDirectory()+"castle_tower_normal.png")


Define Tower = CreateIlluminatedSprite(#Sprite_Tower_DiffuseMap, #Sprite_Tower_NormalMap)

CreateSpriteLight(#SpriteLight_Ambient, $202020) ;- Base illumination for all illuminated Sprites

Define Light_Orange = CreateSpriteLight(#SpriteLight_Point, $80C0FF, 1.0, 800)	;- Point-light illumination
Define Light_Green   = CreateSpriteLight(#SpriteLight_Point, $40FF20, 1.0, 0, 0, 0, 300)	;- Point-light illumination with shorter range

Define Time.f

Repeat
	
	Repeat
		
		Select WindowEvent()
			Case #PB_Event_None
				Break
			Case #PB_Event_CloseWindow
				Break 2
		EndSelect
		
	ForEver
	
	Time = ElapsedMilliseconds()/2000
	
	ClearScreen($003010)
	
	SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendInvertSourceAlpha)
	
	If Light_Orange
		SpriteLightPosition(Light_Orange, 400+Sin(Time*2)*150, 300+Cos(Time*2)*100, 20)
		DisplayTransparentSprite(#Sprite_LightPoint, 400+Sin(Time*2)*150-8, 300+Cos(Time*2)*100-8, 255, $80C0FF)
	EndIf
	
	If Light_Green
		If WindowMouseX(#Window) = -1 And  WindowMouseY(#Window) = -1
			SpriteLightIntensity(Light_Green, 0.0)
		Else
			SpriteLightIntensity(Light_Green, 1.0)
			SpriteLightPosition(Light_Green, WindowMouseX(#Window), WindowMouseY(#Window), 20)
			DisplayTransparentSprite(#Sprite_LightPoint, WindowMouseX(#Window)-8, WindowMouseY(#Window)-8, 255, $40FF20)
		EndIf
	EndIf
	
	DisplayIlluminatedSprite(Tower, 400, 300)
	
	DisplayTransparentSprite(#Sprite_Tower_DiffuseMap, 100-SpriteWidth(#Sprite_Tower_DiffuseMap)/2, 300-SpriteHeight(#Sprite_Tower_DiffuseMap)/2)
	DisplayTransparentSprite(#Sprite_Tower_NormalMap, 700-SpriteWidth(#Sprite_Tower_NormalMap)/2, 300-SpriteHeight(#Sprite_Tower_NormalMap)/2)
	
	If StartDrawing(ScreenOutput())
		DrawingMode(#PB_2DDrawing_Transparent)
		DrawingFont(FontID(#Font))
		DrawText(30, 30, "Sprite: Diffuse map")
		DrawText(330, 30, "IlluminatedSprite")
		DrawText(630, 30, "Sprite: Normal map")
		StopDrawing()
	EndIf
	
	FlipBuffers()
	
ForEver

End
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
Mr.L
Enthusiast
Enthusiast
Posts: 146
Joined: Sun Oct 09, 2011 7:39 am

Re: IlluminatedSprite - Lighting effects with sprites only

Post by Mr.L »

Thanks, STARGÅTE! This is great - and looks fantastic!
User avatar
Mijikai
Addict
Addict
Posts: 1519
Joined: Sun Sep 11, 2016 2:17 pm

Re: IlluminatedSprite - Lighting effects with sprites only

Post by Mijikai »

Wow, thank you :)
Very nice effect.
User avatar
minimy
Enthusiast
Enthusiast
Posts: 597
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Re: IlluminatedSprite - Lighting effects with sprites only

Post by minimy »

Awesome!!! 3D into 2D. Really amazing work! :shock:
Thanks STARGÅTE for share!! :D
This open doors to new horizons in 2D games.
If translation=Error: reply="Sorry, Im Spanish": Endif
threedslider
Enthusiast
Enthusiast
Posts: 393
Joined: Sat Feb 12, 2022 7:15 pm

Re: IlluminatedSprite - Lighting effects with sprites only

Post by threedslider »

That is very nice effects ! :D

Something in PB has endless of possibility :mrgreen:

So thanks for sharing and you have done a great job :wink:
Post Reply