Code: Alles auswählen
;- 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 = Pow(Cos(ACos(Dot)*1.0),1.01)
Else
DotWeighted = DotXY * Pow(Cos(ACos(Dot)*1.0),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