- screen layout : View -> Layers -> Sprite
- view functions: Move, Rotate
- layer functions: Hide, Move, Scale, Rotate
- layer options: blend, filter, order, shadow
- sprite functions: Hide, Move, Scale, Rotate, Deform, Skew, Flip
- sprite options: HotSpot per frame, multi-frame animation, color gradient
Code: Select all
;:=============================================================================
;:- SpriteEngine.pbi
;:- Author : Eddy
;:- Date : September 25, 2013
;:- Compiler : PureBasic 5.20 LTS
;:- Target OS : Mac, Linux, Windows
;:- Source --------------------------------------------------------------------
;:- http://www.purebasic.fr/english/viewtopic.php?f=40&t=56507
;:=============================================================================
EnableExplicit
Structure SP_DEFORM
x.f[4]
y.f[4]
EndStructure
Structure SP_FRAME
u.f[4]
v.f[4]
Width.f
Height.f
HotSpotX.f
HotSpotY.f
EndStructure
Structure SP_ATLAS
Atlas.i
Texture.i
Array Frames.SP_FRAME(0)
EndStructure
Structure SP_SPRITE
Sprite.i
*ParentLayer.SP_LAYER
x.f
y.f
ScaleX.f
ScaleY.f
Angle.f
State.i
Frame.i
Colors.i[4]
*Deform.SP_DEFORM
EndStructure
Structure SP_LAYER
Layer.i
StructureUnion
LayerPivot.i
LayerRoll.i
LayerScale.i
EndStructureUnion
LayerOffset.i
LayerPosition.i
LayerShadow.i
Mesh.i
MeshShadow.i
Material.i
MaterialShadow.i
MaterialBlend.i
MaterialFilter.i
*Atlas.SP_ATLAS
Map *AttachedSprites.SP_SPRITE()
*ParentView.SP_VIEW
x.f
y.f
ScaleX.f
ScaleY.f
State.i
ShadowColor.i
ShadowOffsetX.f
ShadowOffsetY.f
EndStructure
Structure SP_VIEW
View.i
ViewPivot.i
ViewOffset.i
StructureUnion
ViewPosition.i
ViewRoll.i
EndStructureUnion
Camera.i
Map *AttachedLayers.SP_LAYER()
x.f
y.f
CameraX.i
CameraY.i
CameraWidth.i
CameraHeight.i
OffsetX.f
OffsetY.f
ScaleX.f
ScaleY.f
EndStructure
Structure SP_ENGINE
Map Atlases.SP_ATLAS()
Map Views.SP_VIEW()
Map Layers.SP_LAYER()
Map Sprites.SP_SPRITE()
*DefaultSprite.SP_SPRITE
*DefaultLayer.SP_LAYER
Array OrderUV.i(3, 3)
EndStructure
Global SpriteEngine.SP_ENGINE
Macro SP_Colon
:
EndMacro
Macro SP_Override(Function)
SP_Colon#Macro Function#SP_Colon#SP_#Function#SP_Colon#EndMacro
EndMacro
SP_Override(SpriteID)
SP_Override(RotateSprite)
SP_Override(FreeSprite)
Enumeration
#Sprite_Hidden=%1
#Sprite_FlipX=%10
#Sprite_FlipY=%100
#Sprite_FlipXY=#Sprite_FlipX|#Sprite_FlipY
#Sprite_Disabled=%1000
#Layer_Hidden=%1
#Layer_Shadow=%10
EndEnumeration
Procedure.i ViewID(View)
ProcedureReturn FindMapElement(SpriteEngine\Views(), ""+View)
EndProcedure
Procedure.i LayerID(Layer)
ProcedureReturn FindMapElement(SpriteEngine\Layers(), ""+Layer)
EndProcedure
Procedure.i SpriteID(Sprite)
ProcedureReturn FindMapElement(SpriteEngine\Sprites(), ""+Sprite)
EndProcedure
Procedure.i AtlasID(Atlas)
ProcedureReturn FindMapElement(SpriteEngine\Atlases(), ""+Atlas)
EndProcedure
;---------- COLOR
Macro ColorRGB(ColorRGBA)
RGB(Red(ColorRGBA), Green(ColorRGBA), Blue(ColorRGBA))
EndMacro
Macro ColorRGBA(ColorRGB, Alpha)
RGBA(Red(ColorRGB), Green(ColorRGB), Blue(ColorRGB), Alpha)
EndMacro
;---------- ATLAS
Procedure FreeAtlas(Atlas)
Protected *Atlas.SP_ATLAS=AtlasID(Atlas)
;free resources
FreeTexture(*Atlas\Texture)
DeleteMapElement(SpriteEngine\Atlases(), ""+Atlas)
EndProcedure
Procedure.i CreateAtlas(Atlas, Width, Height)
Protected Result
If Atlas=#PB_Any
Static AtlasNumber=$FFFF : AtlasNumber+103 : Atlas=AtlasNumber
AddMapElement(SpriteEngine\Atlases(), ""+Atlas)
Result=Atlas
Else
If AtlasID(Atlas) : FreeAtlas(Atlas) : EndIf
Result=AddMapElement(SpriteEngine\Atlases(), ""+Atlas)
EndIf
With SpriteEngine\Atlases()
\Atlas=Atlas
\Texture=CreateTexture(#PB_Any, Width, Height)
\Frames(0)\u[0]=0 : \Frames(0)\v[0]=0
\Frames(0)\u[1]=1 : \Frames(0)\v[1]=0
\Frames(0)\u[2]=1 : \Frames(0)\v[2]=1
\Frames(0)\u[3]=0 : \Frames(0)\v[3]=1
\Frames(0)\Width=Width
\Frames(0)\Height=Height
ProcedureReturn Result
EndWith
EndProcedure
Procedure.i LoadAtlas(Atlas, ImageFile.s, Quality=2)
Protected TextureImage=LoadImage(#PB_Any, ImageFile)
Protected TextureW=ImageWidth(TextureImage)
Protected TextureH=ImageHeight(TextureImage)
If Quality>0
;power of 2 texture size improves rendering quality
TextureW=Pow(2, Round(Log(TextureW)/Log(2), #PB_Round_Up))
TextureH=Pow(2, Round(Log(TextureH)/Log(2), #PB_Round_Up))
;texture square size improves compatibility with some GPU
If Quality>1
If TextureW<TextureH : TextureW=TextureH : Else : TextureH=TextureW : EndIf
EndIf
EndIf
Protected Result=CreateAtlas(Atlas, TextureW, TextureH)
With SpriteEngine\Atlases()
StartDrawing(TextureOutput(\Texture))
DrawingMode(#PB_2DDrawing_AlphaBlend)
DrawImage(ImageID(TextureImage), 0, 0)
StopDrawing()
EndWith
FreeImage(TextureImage)
ProcedureReturn Result
EndProcedure
Procedure.i AtlasOutput(Atlas)
Protected *Atlas.SP_ATLAS=AtlasID(Atlas)
ProcedureReturn TextureOutput(*Atlas\Texture)
EndProcedure
Procedure.i SetAtlasFrame(Atlas, Frame, x, y, Width, Height, HotSpotX=0, HotSpotY=0, IsTurned=#False)
Protected *Atlas.SP_ATLAS=AtlasID(Atlas)
With *Atlas
If Frame=#PB_Any
Frame=ArraySize(\Frames())+1
EndIf
If Frame> ArraySize(\Frames())
ReDim \Frames(Frame)
EndIf
If IsTurned : x-1 : EndIf
Protected tw.f=TextureWidth(\Texture)
Protected th.f=TextureHeight(\Texture)
Protected u0.f=x/tw, v0.f=y/th
Protected u1.f=(x+Width)/tw, v1.f=(y+Height)/th
EndWith
With *Atlas\Frames(Frame)
If IsTurned
\u[0]=u1 : \v[0]=v0
\u[1]=u1 : \v[1]=v1
\u[2]=u0 : \v[2]=v1
\u[3]=u0 : \v[3]=v0
\Width=Height
\Height=Width
\HotSpotX=HotSpotY
\HotSpotY=\Height-HotSpotX
Else
\u[0]=u0 : \v[0]=v0
\u[1]=u1 : \v[1]=v0
\u[2]=u1 : \v[2]=v1
\u[3]=u0 : \v[3]=v1
\Width=Width
\Height=Height
\HotSpotX=HotSpotX
\HotSpotY=HotSpotY
EndIf
EndWith
ProcedureReturn Frame
EndProcedure
Procedure StartAtlas(Atlas, Margin=1)
EndProcedure
Procedure StopAtlas()
EndProcedure
;---------- SPRITE
Procedure FreeSprite(Sprite)
Protected *Sprite.SP_Sprite=SpriteID(Sprite)
;detach parent
FindMapElement(*Sprite\ParentLayer\AttachedSprites(), ""+Sprite)
DeleteMapElement(*Sprite\ParentLayer\AttachedSprites())
;free resources
If *Sprite\Deform : FreeMemory(*Sprite\Deform) : EndIf
DeleteMapElement(SpriteEngine\Sprites(), ""+Sprite)
EndProcedure
Procedure.i CreateAnimatedSprite(Sprite, Layer, x.f, y.f, Frame=#PB_Default, Color=#PB_Default, Transparency=#PB_Default, Angle.f=#PB_Default, ScaleX.f=#PB_Default, ScaleY.f=#PB_Default)
Protected Result
If Sprite=#PB_Any
Static SpriteNumber=$FFFF : SpriteNumber+103 : Sprite=SpriteNumber
AddMapElement(SpriteEngine\Sprites(), ""+Sprite)
Result=Sprite
Else
If SpriteID(Sprite) : FreeSprite(Sprite) : EndIf
Result=AddMapElement(SpriteEngine\Sprites(), ""+Sprite)
EndIf
With SpriteEngine\Sprites()
\Sprite=Sprite
\ParentLayer=LayerID(Layer)
\ParentLayer\AttachedSprites(""+Sprite)=SpriteEngine\Sprites()
Protected cornerIndex, newColor=Color, newTransparency=Transparency, defaultColor
For cornerIndex=0 To 3
defaultColor=SpriteEngine\DefaultSprite\Colors[cornerIndex]
If Color=#PB_Default : newColor=ColorRGB(defaultColor) : EndIf
If Transparency=#PB_Default : newTransparency=Alpha(defaultColor) : EndIf
\Colors[cornerIndex]=ColorRGBA(newColor, newTransparency)
Next
If Frame=#PB_Default : \Frame=SpriteEngine\DefaultSprite\Frame : Else : \Frame=Frame : EndIf
If x=#PB_Default : \x=SpriteEngine\DefaultSprite\x : Else : \x=x : EndIf
If y=#PB_Default : \y=SpriteEngine\DefaultSprite\y : Else : \y=y : EndIf
If Angle=#PB_Default : \Angle=SpriteEngine\DefaultSprite\Angle : Else : \Angle=Angle : EndIf
If ScaleX=#PB_Default : \ScaleX=SpriteEngine\DefaultSprite\ScaleX : Else : \ScaleX=ScaleX : EndIf
If ScaleY=#PB_Default : \ScaleY=SpriteEngine\DefaultSprite\ScaleY : Else : \ScaleY=ScaleY : EndIf
ProcedureReturn Result
EndWith
EndProcedure
Procedure HideSprite(Sprite, IsHidden)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
*Sprite\State & ~#Sprite_Hidden | Bool(IsHidden)*#Sprite_Hidden
EndProcedure
Procedure DisableSprite(Sprite, IsDisabled)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
*Sprite\State & ~#Sprite_Disabled | Bool(IsDisabled)*#Sprite_Disabled
EndProcedure
Procedure MoveSprite(Sprite, x.f, y.f, Mode=#PB_Relative)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
If Mode = #PB_Relative
*Sprite\x+x
*Sprite\y+y
Else
*Sprite\x=x
*Sprite\y=y
EndIf
EndProcedure
Procedure RotateSprite(Sprite, Angle.f, Mode=#PB_Relative)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
If Mode=#PB_Relative
*Sprite\Angle+Angle
Else
*Sprite\Angle=Angle
EndIf
EndProcedure
Procedure ScaleSprite(Sprite, ScaleX.f, ScaleY.f, Mode=#PB_Absolute)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
If Mode=#PB_Absolute
*Sprite\ScaleX=ScaleX
*Sprite\ScaleY=ScaleY
Else
*Sprite\ScaleX+ScaleX
*Sprite\ScaleY+ScaleY
EndIf
EndProcedure
Procedure FlipSprite(Sprite, FlipX=#False, FlipY=#False)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
With *Sprite
FlipX & ~(#Sprite_FlipX|#Sprite_FlipY)
If FlipX : \State | #Sprite_FlipX : EndIf
If FlipY : \State | #Sprite_FlipY : EndIf
EndWith
EndProcedure
Procedure DeformSprite(Sprite, x0.f, y0.f, x1.f, y1.f, x2.f, y2.f, x3.f, y3.f)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
With *Sprite\Deform
*Sprite\Deform=AllocateMemory(SizeOf(SP_DEFORM))
\x[0]=x0 : \y[0]=y0
\x[1]=x1 : \y[1]=y1
\x[2]=x2 : \y[2]=y2
\x[3]=x3 : \y[3]=y3
EndWith
EndProcedure
Procedure SkewSprite(Sprite, First.f, Second.f, Vertical=#False)
If Vertical
DeformSprite(Sprite, 0, First, 0, Second, 0, Second, 0, First)
Else
DeformSprite(Sprite, First, 0, First, 0, Second, 0, Second, 0)
EndIf
EndProcedure
Procedure LayoutSprite(Sprite, Left.f=#PB_Ignore, Top.f=#PB_Ignore, Right.f=#PB_Ignore, Bottom.f=#PB_Ignore, InPixels=#True)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
With *Sprite
Protected w=\ParentLayer\ParentView\CameraWidth
Protected h=\ParentLayer\ParentView\CameraHeight
Protected LayoutX, LayoutY
If Not InPixels
If Top<>#PB_Ignore : Top*h : EndIf
If Bottom<>#PB_Ignore : Bottom*h : EndIf
If Right<>#PB_Ignore : Right*w : EndIf
If Left<>#PB_Ignore : Left*w : EndIf
EndIf
LayoutX+1*Bool(Left<>#PB_Ignore)+2*Bool(Right<>#PB_Ignore)
LayoutY+1*Bool(Top<>#PB_Ignore)+2*Bool(Bottom<>#PB_Ignore)
Select LayoutX
Case 0 :
Case 1 :
Case 2 :
Case 3 :
EndSelect
EndWith
EndProcedure
Procedure SetSpriteTransparency(Sprite, Transparency, CornerIndex=#PB_Any)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
If CornerIndex=#PB_Any
For CornerIndex=0 To 3
*Sprite\Colors[CornerIndex]=ColorRGBA(ColorRGB(*Sprite\Colors[CornerIndex]), Transparency)
Next
Else
*Sprite\Colors[CornerIndex]=ColorRGBA(ColorRGB(*Sprite\Colors[CornerIndex]), Transparency)
EndIf
EndProcedure
Procedure SetSpriteColor(Sprite, Color, CornerIndex=#PB_Any)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
If CornerIndex=#PB_Any
For CornerIndex=0 To 3
*Sprite\Colors[CornerIndex]=ColorRGBA(Color, Alpha(*Sprite\Colors[CornerIndex]))
Next
Else
*Sprite\Colors[CornerIndex]=ColorRGBA(Color, Alpha(*Sprite\Colors[CornerIndex]))
EndIf
EndProcedure
Procedure SetSpriteFrame(Sprite, Frame)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
*Sprite\Frame=Frame
EndProcedure
Procedure.f SpriteAngle(Sprite)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn *Sprite\Angle
EndProcedure
Procedure.f SpriteX(Sprite)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn *Sprite\x
EndProcedure
Procedure.f SpriteY(Sprite)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn *Sprite\y
EndProcedure
Procedure.f SpriteScaleX(Sprite)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn *Sprite\ScaleX
EndProcedure
Procedure.f SpriteScaleY(Sprite)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn *Sprite\ScaleY
EndProcedure
Procedure.i SpriteColor(Sprite, CornerIndex=0)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn ColorRGB(*Sprite\Colors[CornerIndex])
EndProcedure
Procedure.i SpriteTransparency(Sprite, CornerIndex=0)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn Alpha(*Sprite\Colors[CornerIndex])
EndProcedure
Procedure.i SpriteFrame(Sprite)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn *Sprite\Frame
EndProcedure
Procedure.i SpriteHidden(Sprite)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn Bool(*Sprite\State & #Sprite_Hidden)
EndProcedure
Procedure.i SpriteState(Sprite)
Protected *Sprite.SP_SPRITE=SpriteID(Sprite)
ProcedureReturn *Sprite\State
EndProcedure
;---------- LAYER
Procedure FreeLayer(Layer)
Protected *Layer.SP_LAYER=LayerID(Layer)
;delete dependencies
ForEach *Layer\AttachedSprites()
FreeSprite(*Layer\AttachedSprites()\Sprite)
Next
;detach parent
FindMapElement(*Layer\ParentView\AttachedLayers(), ""+Layer)
DeleteMapElement(*Layer\ParentView\AttachedLayers())
;free resources
FreeNode(*Layer\LayerPivot)
FreeNode(*Layer\LayerOffset)
FreeNode(*Layer\LayerPosition)
FreeNode(*Layer\LayerShadow)
FreeMesh(*Layer\Mesh)
FreeMesh(*Layer\MeshShadow)
FreeMaterial(*Layer\Material)
FreeMaterial(*Layer\MaterialShadow)
DeleteMapElement(SpriteEngine\Layers(), ""+Layer)
EndProcedure
Procedure.i CreateLayer(Layer, View, *AtlasID, MaterialBlend=#PB_Default, MaterialFilter=#PB_Default)
Protected Result
If Layer=#PB_Any
Static LayerNumber=$FFFF : LayerNumber+103 : Layer=LayerNumber
AddMapElement(SpriteEngine\Layers(), ""+Layer)
Result=Layer
Else
If LayerID(Layer) : FreeLayer(Layer) : EndIf
Result=AddMapElement(SpriteEngine\Layers(), ""+Layer)
EndIf
With SpriteEngine\Layers()
\Layer=Layer
\Atlas=*AtlasID
\Material=CreateMaterial(#PB_Any, TextureID(\Atlas\Texture))
MaterialCullingMode(\Material, #PB_Material_NoCulling)
If MaterialBlend=#PB_Default : MaterialBlend=SpriteEngine\DefaultLayer\MaterialBlend : EndIf
If MaterialFilter=#PB_Default : MaterialFilter=SpriteEngine\DefaultLayer\MaterialFilter : EndIf
MaterialFilteringMode(\Material, MaterialFilter)
MaterialBlendingMode(\Material, MaterialBlend)
DisableMaterialLighting(\Material, #True)
\MaterialShadow=CopyMaterial(\Material, #PB_Any)
\Mesh=CreateMesh(#PB_Any, #PB_Mesh_TriangleList, #PB_Mesh_Dynamic)
SetMeshMaterial(\Mesh, MaterialID(\Material))
MeshVertexPosition(0, 0, 0)
MeshVertexNormal(0, 0, 0)
MeshVertexTangent(0, 0, 0)
MeshVertexTextureCoordinate(0, 0)
MeshVertexColor(RGBA(255, 255, 255, 255))
MeshVertexPosition(1, 0, 0)
MeshVertexPosition(1, 1, 0)
MeshFace(0, 1, 2)
FinishMesh(0)
NormalizeMesh(\Mesh)
\MeshShadow=CreateMesh(#PB_Any, #PB_Mesh_TriangleList, #PB_Mesh_Dynamic)
SetMeshMaterial(\MeshShadow, MaterialID(\MaterialShadow))
MeshVertexPosition(0, 0, 0)
MeshVertexNormal(0, 0, 0)
MeshVertexTangent(0, 0, 0)
MeshVertexTextureCoordinate(0, 0)
MeshVertexColor(RGBA(255, 255, 255, 255))
MeshVertexPosition(1, 0, 0)
MeshVertexPosition(1, 1, 0)
MeshFace(0, 1, 2)
FinishMesh(0)
NormalizeMesh(\MeshShadow)
\LayerShadow=CreateNode(#PB_Any)
\LayerPosition=CreateNode(#PB_Any)
\LayerOffset=CreateNode(#PB_Any)
\LayerPivot=CreateNode(#PB_Any)
SetRenderQueue(MeshID(\MeshShadow), 1, 10)
SetRenderQueue(MeshID(\Mesh), 1, 100)
AttachNodeObject(\LayerPosition, NodeID(\LayerShadow))
AttachNodeObject(\LayerPosition, MeshID(\Mesh))
AttachNodeObject(\LayerOffset, NodeID(\LayerPosition))
AttachNodeObject(\LayerPivot, NodeID(\LayerOffset))
\ParentView=ViewID(View)
\ParentView\AttachedLayers(""+Layer)=SpriteEngine\Layers()
AttachNodeObject(\ParentView\ViewPivot, NodeID(\LayerPivot))
ScaleNode(\LayerOffset, \ParentView\ScaleX, \ParentView\ScaleY, 1)
MoveNode(\LayerOffset, \ParentView\OffsetX, \ParentView\OffsetY, 500)
ProcedureReturn Result
EndWith
EndProcedure
Procedure HideLayer(Layer, IsHidden)
Protected *Layer.SP_LAYER=LayerID(Layer)
With *Layer
If IsHidden
If Not (\State & #Layer_Hidden)
\State | #Layer_Hidden
DetachNodeObject(\LayerPosition, MeshID(\Mesh))
If (\State & #Layer_Shadow) : DetachNodeObject(\LayerShadow, MeshID(\MeshShadow)) : EndIf
EndIf
Else
If \State & #Layer_Hidden
\State & ~#Layer_Hidden
AttachNodeObject(\LayerPosition, MeshID(\Mesh))
If (\State & #Layer_Shadow) : AttachNodeObject(\LayerShadow, MeshID(\MeshShadow)) : EndIf
EndIf
EndIf
EndWith
EndProcedure
Procedure MoveLayer(Layer, x.f, y.f, Mode=#PB_Relative)
Protected *Layer.SP_LAYER=LayerID(Layer)
If Mode = #PB_Relative
*Layer\x+x
*Layer\y+y
Else
*Layer\x=x
*Layer\y=y
EndIf
MoveNode(*Layer\LayerPosition, x, y, 0, Mode)
EndProcedure
Procedure RotateLayer(Layer, Angle.f, Mode=#PB_Relative)
Protected *Layer.SP_LAYER=LayerID(Layer)
RotateNode(*Layer\LayerRoll, 0, 0, Angle, Mode)
EndProcedure
Procedure ScaleLayer(Layer, ScaleX.f, ScaleY.f, Mode=#PB_Absolute)
Protected *Layer.SP_LAYER=LayerID(Layer)
If Mode=#PB_Absolute
*Layer\ScaleX=ScaleX
*Layer\ScaleY=ScaleY
Else
*Layer\ScaleX+ScaleX
*Layer\ScaleY+ScaleY
EndIf
ScaleNode(*Layer\LayerScale, ScaleX, ScaleY, 1, Mode)
EndProcedure
Procedure SetLayerShadow(Layer, CastShadow, Color.f=#PB_Ignore, Transparency.f=#PB_Ignore, OffsetX.f=#PB_Ignore, OffsetY.f=#PB_Ignore)
Protected *Layer.SP_LAYER=LayerID(Layer)
With *Layer
If CastShadow
If Not (\State & #Layer_Shadow)
\State | #Layer_Shadow
If Not (\State & #Layer_Hidden)
AttachNodeObject(\LayerShadow, MeshID(\MeshShadow))
EndIf
EndIf
If Color.f<>#PB_Ignore : \ShadowColor=ColorRGBA(Color, Alpha(\ShadowColor)) : EndIf
If Transparency.f<>#PB_Ignore : \ShadowColor=ColorRGBA(ColorRGB(\ShadowColor), Transparency) : EndIf
If OffsetX.f<>#PB_Ignore : \ShadowOffsetX=OffsetX : EndIf
If OffsetY.f<>#PB_Ignore : \ShadowOffsetY=OffsetY : EndIf
MoveNode(\LayerShadow, \ShadowOffsetX, \ShadowOffsetY, 0, #PB_Absolute)
MaterialBlendingMode(\MaterialShadow, #PB_Material_AlphaBlend)
Else
If \State & #Layer_Shadow
\State & ~#Layer_Shadow
DetachNodeObject(\LayerShadow, MeshID(\MeshShadow))
EndIf
EndIf
EndWith
EndProcedure
Procedure SetLayerOrder(Layer, Order, ShadowOrder=#PB_Ignore)
Protected *Layer.SP_LAYER=LayerID(Layer)
With *Layer
If Order<>#PB_Ignore
If Order<0 : Order=0 : EndIf
If Order>10000 : Order=10000 : EndIf
SetRenderQueue(MeshID(\Mesh), 1, Order)
EndIf
If ShadowOrder<>#PB_Ignore
If ShadowOrder<0 : ShadowOrder=0 : EndIf
If ShadowOrder>10000 : ShadowOrder=10000 : EndIf
SetRenderQueue(MeshID(\MeshShadow), 1, ShadowOrder)
EndIf
EndWith
EndProcedure
Procedure.f LayerAngle(Layer)
Protected *Layer.SP_LAYER=LayerID(Layer)
ProcedureReturn NodeRoll(*Layer\LayerRoll)
EndProcedure
Procedure.f LayerX(Layer)
Protected *Layer.SP_LAYER=LayerID(Layer)
ProcedureReturn *Layer\x
EndProcedure
Procedure.f LayerY(Layer)
Protected *Layer.SP_LAYER=LayerID(Layer)
ProcedureReturn *Layer\y
EndProcedure
Procedure.f LayerScaleX(Layer)
Protected *Layer.SP_LAYER=LayerID(Layer)
ProcedureReturn *Layer\ScaleX
EndProcedure
Procedure.f LayerScaleY(Layer)
Protected *Layer.SP_LAYER=LayerID(Layer)
ProcedureReturn *Layer\ScaleY
EndProcedure
Procedure.i LayerHidden(Layer)
Protected *Layer.SP_LAYER=LayerID(Layer)
ProcedureReturn Bool(*Layer\State & #Layer_Hidden)
EndProcedure
Procedure.i LayerState(Layer)
Protected *Layer.SP_LAYER=LayerID(Layer)
ProcedureReturn *Layer\State
EndProcedure
;---------- VIEW
Procedure FreeView(View)
Protected *View.SP_VIEW=ViewID(View)
;delete dependencies
ForEach *View\AttachedLayers()
FreeLayer(*View\AttachedLayers()\Layer)
Next
;free resources
FreeCamera(*View\Camera)
FreeNode(*View\ViewPivot)
DeleteMapElement(SpriteEngine\Views(), ""+View)
EndProcedure
Procedure.i CreateView(View, BackColor=0, IsStretched=#False, x.f=0, y.f=0, w.f=100, h.f=100)
Protected Result
If View=#PB_Any
Static ViewNumber=$FFFF : ViewNumber+103 : View=ViewNumber
AddMapElement(SpriteEngine\Views(), ""+View)
Result=View
Else
If ViewID(View) : FreeView(View) : EndIf
Result=AddMapElement(SpriteEngine\Views(), ""+View)
EndIf
With SpriteEngine\Views()
\View=View
\Camera=CreateCamera(#PB_Any, x, y, w, h)
\CameraX=CameraViewX(\Camera)
\CameraY=CameraViewY(\Camera)
\CameraWidth=CameraViewWidth(\Camera)
\CameraHeight=CameraViewHeight(\Camera)
CameraBackColor(\Camera, BackColor)
CameraRange(\Camera, 0.1, 1000)
CameraProjectionMode(\Camera, #PB_Camera_Orthographic)
SetOrientation(CameraID(\Camera), 1, 0, 0, 0)
Protected hidden=CreateEntity(#PB_Any, MeshID(CreateCube(#PB_Any, 100)), #PB_Material_None)
ScaleEntity(hidden, 100, 100, 0.01)
HideEntity(hidden, 1)
MoveEntity(hidden, 0, 0, 500)
\ViewPivot=CreateNode(#PB_Any)
\ViewOffset=CreateNode(#PB_Any)
\ViewPosition=CreateNode(#PB_Any)
AttachNodeObject(\ViewPosition, CameraID(\Camera))
AttachNodeObject(\ViewOffset, NodeID(\ViewPosition))
AttachNodeObject(\ViewPivot, NodeID(\ViewOffset))
AttachNodeObject(\ViewPivot, EntityID(hidden))
RenderWorld()
If MousePick(\Camera, CameraViewX(\Camera), CameraViewY(\Camera))
\OffsetX=PickX()
\OffsetY=PickY()
If IsStretched
\ScaleX=Abs(PickX()/(ScreenWidth()/2))
\ScaleY=Abs(PickY()/(ScreenHeight()/2))
Else
\ScaleX=Abs(PickX()/(CameraViewWidth(\Camera)/2))
\ScaleY=Abs(PickY()/(CameraViewHeight(\Camera)/2))
EndIf
FreeEntity(hidden)
EndIf
ScaleNode(\ViewOffset, \ScaleX, \ScaleY, 1)
Static newViewPos : newViewPos+$FFFF
MoveNode(\ViewPivot, newViewPos, newViewPos, newViewPos)
ProcedureReturn Result
EndWith
EndProcedure
Procedure ViewCameraMode(View, RenderMode=#PB_Camera_Textured)
Protected *View.SP_VIEW=ViewID(View)
CameraRenderMode(*View\Camera, RenderMode)
EndProcedure
Procedure MoveView(View, x.f, y.f, Mode=#PB_Relative)
Protected *View.SP_VIEW=ViewID(View)
If Mode = #PB_Relative
*View\x+x
*View\y+y
Else
*View\x=x
*View\y=y
EndIf
ProcedureReturn MoveNode(*View\ViewPosition, x, y, 0, Mode)
EndProcedure
Procedure RotateView(View, Angle.f, Mode=#PB_Relative)
Protected *View.SP_VIEW=ViewID(View)
RotateNode(*View\ViewRoll, 0, 0, Angle, Mode)
EndProcedure
Procedure.f ViewAngle(View)
Protected *View.SP_VIEW=ViewID(View)
ProcedureReturn NodeRoll(*View\ViewRoll)
EndProcedure
Procedure.f ViewX(View)
Protected *View.SP_VIEW=ViewID(View)
ProcedureReturn *View\x
EndProcedure
Procedure.f ViewY(View)
Protected *View.SP_VIEW=ViewID(View)
ProcedureReturn *View\y
EndProcedure
Procedure.i ViewCameraX(View)
Protected *View.SP_VIEW=ViewID(View)
ProcedureReturn *View\CameraX
EndProcedure
Procedure.i ViewCameraY(View)
Protected *View.SP_VIEW=ViewID(View)
ProcedureReturn *View\CameraY
EndProcedure
Procedure.i ViewCameraWidth(View)
Protected *View.SP_VIEW=ViewID(View)
ProcedureReturn *View\CameraWidth
EndProcedure
Procedure.i ViewCameraHeight(View)
Protected *View.SP_VIEW=ViewID(View)
ProcedureReturn *View\CameraHeight
EndProcedure
Procedure InitSpriteEngine()
If InitSprite()
With SpriteEngine
\DefaultSprite=AddMapElement(\Sprites(), ""+#PB_Default)
\DefaultLayer=AddMapElement(\Layers(), ""+#PB_Default)
\OrderUV(0, 0)=0 : \OrderUV(0, 1)=1 : \OrderUV(0, 2)=2 : \OrderUV(0, 3)=3 ;Normal
\OrderUV(1, 0)=1 : \OrderUV(1, 1)=0 : \OrderUV(1, 2)=3 : \OrderUV(1, 3)=2 ;Flip X
\OrderUV(2, 0)=3 : \OrderUV(2, 1)=2 : \OrderUV(2, 2)=1 : \OrderUV(2, 3)=0 ;Flip Y
\OrderUV(3, 0)=2 : \OrderUV(3, 1)=3 : \OrderUV(3, 2)=0 : \OrderUV(3, 3)=1 ;Flip X & Y
EndWith
Protected colorOpaque=RGBA(255, 255, 255, 255)
Protected cornerIndex
With SpriteEngine\DefaultSprite
\ScaleX=1
\ScaleY=1
For cornerIndex=0 To 3 : \Colors[cornerIndex]=colorOpaque : Next
EndWith
With SpriteEngine\DefaultLayer
\MaterialBlend=#PB_Material_AlphaBlend
\MaterialFilter=#PB_Material_Bilinear
EndWith
ProcedureReturn #True
EndIf
EndProcedure
Procedure RenderSprites()
Protected *View.SP_VIEW, *Layer.SP_LAYER, *Sprite.SP_SPRITE
Protected vertexIndex
ForEach SpriteEngine\Views()
ForEach SpriteEngine\Views()\AttachedLayers()
*Layer=SpriteEngine\Views()\AttachedLayers()
If *Layer\State & #Layer_Hidden : Continue : EndIf
vertexIndex=0
UpdateMesh(*Layer\Mesh, 0)
ForEach *Layer\AttachedSprites()
*Sprite=*Layer\AttachedSprites()
If *Sprite\State & #Sprite_Hidden : Continue : EndIf
With *Sprite
Protected *Frame.SP_FRAME=\ParentLayer\Atlas\Frames(\Frame)
Protected x0.f, y0.f, x1.f, y1.f, hx.f, hy.f, i, FlipMode=0
Dim x.f(3) : Dim y.f(3)
Dim u.f(3) : Dim v.f(3)
hx=*Frame\HotSpotX
hy=*Frame\HotSpotY
If Not (\State & #Sprite_FlipXY)
CopyMemory(@*Frame\u[0], u(), 4*SizeOf(Float))
CopyMemory(@*Frame\v[0], v(), 4*SizeOf(Float))
Else
FlipMode=1*Bool(\State & #Sprite_FlipX)+2*Bool(\State & #Sprite_FlipY)
If \State & #Sprite_FlipX : hx=*Frame\Width-hx : EndIf
If \State & #Sprite_FlipY : hy=*Frame\Height-hy : EndIf
For i=0 To 3
u(i)=*Frame\u[SpriteEngine\OrderUV(FlipMode, i)]
v(i)=*Frame\v[SpriteEngine\OrderUV(FlipMode, i)]
Next
EndIf
If \Angle=0 And \Deform=0
x0=\x-hx*\ScaleX : x1=x0+*Frame\Width*\ScaleX
y0=\y-hy*\ScaleY : y1=y0+*Frame\Height*\ScaleY
x(0)=x0 : y(0)=y0 : x(1)=x1 : y(1)=y0
x(2)=x1 : y(2)=y1 : x(3)=x0 : y(3)=y1
Else
x0=-hx*\ScaleX : x1=x0+*Frame\Width*\ScaleX
y0=-hy*\ScaleY : y1=y0+*Frame\Height*\ScaleY
x(0)=x0 : y(0)=y0 : x(1)=x1 : y(1)=y0
x(2)=x1 : y(2)=y1 : x(3)=x0 : y(3)=y1
If \Deform
For i=0 To 3
x(i)+\Deform\x[i]
y(i)+\Deform\y[i]
Next
EndIf
Protected radian.f=Radian(\Angle)
Protected cos.f=Cos(radian), sin.f=Sin(radian)
For i=0 To 3
Protected px.f=x(i), py.f=y(i)
x(i)=\x+cos*px-sin*py
y(i)=\y+sin*px+cos*py
Next
EndIf
For i=0 To 3
MeshVertexPosition(x(i), y(i), 0)
MeshVertexTextureCoordinate(u(i), v(i))
MeshVertexColor(\Colors[i])
Next
MeshFace(vertexIndex, vertexIndex+1, vertexIndex+2)
MeshFace(vertexIndex+2, vertexIndex+3, vertexIndex)
vertexIndex+4
EndWith
Next
FinishMesh(0)
With *Layer
If \State & #Layer_Shadow
Dim vertices.PB_MeshVertex(0)
Dim faces.PB_MeshFace(0)
GetMeshData(\Mesh, 0, vertices(), #PB_Mesh_Vertex|#PB_Mesh_Color|#PB_Mesh_UVCoordinate, 0, MeshVertexCount(\Mesh, 0)-1)
GetMeshData(\Mesh, 0, faces(), #PB_Mesh_Face, 0, MeshIndexCount(\Mesh, 0)-1)
UpdateMesh(\MeshShadow, 0)
For i=0 To ArraySize(vertices())
MeshVertexPosition(vertices(i)\x, vertices(i)\y, 0)
MeshVertexTextureCoordinate(vertices(i)\u, vertices(i)\v)
MeshVertexColor(\ShadowColor)
Next
For i=0 To ArraySize(faces())
MeshIndex(faces(i)\Index)
Next
FinishMesh(0)
EndIf
EndWith
Next
Next
EndProcedure