Here is some old test code that I've shared at various times in the forum that I have now updated to handle the changes. It shows a variety of sprite transforms and also allows an interactive transformation of a sprite (on the lower right of window). The window lists what the transformations being shown should be doing so that you will know it's function correctly even if you have nothing to compare it too. I was only able to test things in Windows but if someone could give some feedback on how it works with other OS's (using OpenGL) it would be appreciated.
Transformations with OpenGL allow the sprite to be flipped to see the 'backside' but DirectX doesn't. I have only tried a few of the sample code in the forum that allows some things with DirectX but nothing has worked as a drop in replacement in the sample code below.
Please let me know of any errors or bugs.
Code: Select all
;Program description: Sprite transformation effects
;Author: Demivec
;Written for: Originally for PB 4.61, with Windows OS, may not function correctly for other OS but hopefully does.
;Date Written: 2 July 2012
; Updated: 4 October 2015 for PB 5.40b8 and added some additional features.
;Last Updated: 28 February 2019 for PB 5.70 and added some additional features so it works for OpenGL.
EnableExplicit
CompilerIf Defined(Point, #PB_Structure) = 0
Structure Point
x.i
y.i
EndStructure
CompilerEndIf
Structure Point_f
x.f
y.f
EndStructure
Enumeration sprites
#spr_pb
#spr_corner
#spr_text_1
#spr_text_2
#spr_text_3
#spr_text_4
#spr_mousePointer
#spr_text_descript_1
#spr_text_descript_2
#spr_text_descript_3
#spr_text_descript_4
#spr_text_descript_5
#spr_text_descript_6
#spr_text_descript_7
#spr_text_descript_8
#spr_text_descript_9
#spr_text_descript_10
#spr_text_descript_11
#spr_text_descript_12
EndEnumeration
;check if OpenGL or DirectX is being used for sprites, best guess and not well tested ;)
CompilerIf (#PB_Compiler_OS = #PB_OS_Linux Or #PB_Compiler_OS = #PB_OS_MacOS)
#pb_sprite_subsystem = "OpenGL"
CompilerElseIf #PB_Compiler_OS = #PB_OS_Windows
CompilerIf Subsystem("OpenGL")
#pb_sprite_subsystem = "OpenGL"
CompilerElse
#pb_sprite_subsystem = "DirectX"
CompilerEndIf
CompilerElse
#pb_sprite_subsystem = "Unknown"
CompilerEndIf
Enumeration fonts
#font_large
#font_small
EndEnumeration
Enumeration windows
#win_main
EndEnumeration
Procedure handleError(value, text.s = "Unknown Error.")
If value = 0
MessageRequester("Error", text)
End
EndIf
EndProcedure
;macro to correct 2D sprite Transforms for OpenGL by manipulation of corner coordinate data
Macro TransformSprite2D(spriteID,x1, y1, x2, y2, x3, y3, x4, y4)
CompilerIf #pb_sprite_subsystem = "OpenGL"
TransformSprite(spriteID,x1, y1, x2, -(y3) + (y1) + (y4), x3, -(y2) + (y1) + (y4), x4, y4)
CompilerElse
TransformSprite(spriteID,x1, y1, x2, y2, x3, y3, x4, y4)
CompilerEndIf
EndMacro
;macro to correct 3D sprite Transforms for OpenGL by manipulation of corner coordinate data
Macro TransformSprite3D(spriteID,x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4)
CompilerIf #pb_sprite_subsystem = "OpenGL"
TransformSprite(spriteID,x1, y1, z1, x2, -(y3) + (y1) + (y4), z2, x3, -(y2) + (y1) + (y4), z3, x4, y4, z4)
CompilerElse
TransformSprite(spriteID,x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4)
CompilerEndIf
EndMacro
;macro to ensure transparent sprite colors work for OpenGL by altering the color, then using the correct color
Macro TransparentSpriteColorOK(spriteID, color)
CompilerIf #pb_sprite_subsystem = "OpenGL"
TransparentSpriteColor(spriteID, color - 1)
CompilerEndIf
TransparentSpriteColor(spriteID, color)
EndMacro
;Sprite info: (width, height) = original dimensions, (newWidth, newHeight) = zoom dimensions,
; degree = rotation amount, (cx,cy) = center point for rotation
Procedure MakeSpriteTransformation(spriteID, width, height, cx, cy, degree, newWidth, newHeight)
Protected.Point_f p1, p2, p3, p4
Protected.f wf = width / newWidth, hf = height / newHeight
If degree = 0
;simple transformation: no rotation, only zooming and translation movements possible
p1\x = (( -cx) / wf) - 0.5: p1\y = (( -cy) / hf) - 0.5
p2\x = ((width - cx) / wf) - 0.5: p2\y = (( -cy) / hf) - 0.5
p3\x = ((width - cx) / wf) - 0.5: p3\y = ((height - cy) / hf) - 0.5
p4\x = (( -cx) / wf) - 0.5: p4\y = ((height - cy) / hf) - 0.5
Else
;more complex transformation: rotation (around point cx,cy) and zooming possible
Protected.f angCos = Cos(Radian(degree)), angSin = Sin(Radian(degree))
Protected.f v1, v2, v3, v4, v5, v6, v7, v8
v1 = -cx * angCos: v3 = -cy * angSin
v2 = -cx * angSin: v4 = -cy * angCos
v6 = (width - cx) * angCos: v7 = (height - cy) * angSin
v5 = (width - cx) * angSin: v8 = (height - cy) * angCos
p1\x = ((v1 - v3) / wf) - 0.5: p1\y = ((v4 + v2) / hf) - 0.5
p2\x = ((v6 - v3) / wf) - 0.5: p2\y = ((v4 + v5) / hf) - 0.5
p3\x = ((v6 - v7) / wf) - 0.5: p3\y = ((v8 + v5) / hf) - 0.5
p4\x = ((v1 - v7) / wf) - 0.5: p4\y = ((v8 + v2) / hf) - 0.5
EndIf
TransformSprite2D(spriteID, p1\x, p1\y, p2\x, p2\y, p3\x, p3\y, p4\x, p4\y)
EndProcedure
;iso (squish) rotated clockwise degrees
Procedure MakeSpriteIsoClockwise(spriteID, width, height, cx, cy, degree, newWidth, newHeight)
MakeSpriteTransformation(spriteID, width, height, cx, cy, degree, newWidth, newHeight)
EndProcedure
;iso (squish) rotated counter-clockwise degrees
Procedure MakeSpriteIsoCounterClockwise(spriteID, width, height, cx, cy, degree, newWidth, newHeight)
MakeSpriteTransformation(spriteID, width, height, cx, cy, 360 - degree, newWidth, newHeight)
EndProcedure
Procedure MakeSpriteRotate(SpriteID, cx, cy, degree)
Protected width = SpriteWidth(SpriteID), height = SpriteHeight(SpriteID)
ZoomSprite(SpriteID, #PB_Default, #PB_Default)
MakeSpriteTransformation(spriteID, width, height, cx, cy, degree, width, height)
EndProcedure
Procedure MakeSpriteZoomHorizontal(SpriteID, cx, cy, newWidth)
Protected width = SpriteWidth(SpriteID), height = SpriteHeight(SpriteID)
ZoomSprite(SpriteID, #PB_Default, #PB_Default)
MakeSpriteTransformation(spriteID, width, height, cx, cy, 0, newWidth, height)
EndProcedure
Procedure MakeSpriteZoomVertical(SpriteID, cx, cy, newHeight)
Protected width = SpriteWidth(SpriteID), height = SpriteHeight(SpriteID)
ZoomSprite(SpriteID, #PB_Default, #PB_Default)
MakeSpriteTransformation(spriteID, width, height, cx, cy, 0, width, newHeight)
EndProcedure
Procedure MakeSpriteNormal(SpriteID, cx = 0, cy = 0)
Protected width = SpriteWidth(SpriteID), height = SpriteHeight(SpriteID)
ZoomSprite(SpriteID, #PB_Default, #PB_Default)
MakeSpriteTransformation(spriteID, width, height, cx, cy, 0, width, height)
EndProcedure
handleError(InitSprite(), "Can't open screen & sprite enviroment.")
handleError(InitMouse(), "Can't initialize mouse handler.")
handleError(InitKeyboard(), "Can't initialize keyboard handler.")
handleError(OpenWindow(#win_main, 0, 0, 800, 600, "Sprite Transformations (" + #pb_sprite_subsystem + ")",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered), "Can't open window.")
handleError(OpenWindowedScreen(WindowID(#win_main), 0, 0, 800, 600, 0, 0, 0), "Can't open windowed screen.")
handleError(LoadFont(#font_large, "Arial", 36), "Unable to load font.")
handleError(LoadFont(#font_small, "Arial", 8), "Unable to load font.")
handleError(CreateSprite(#spr_pb, 64, 64), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_pb)), "Can't draw on sprite.")
Box(0,0,64,64,RGB($FF,$FF,$00))
DrawingMode(#PB_2DDrawing_Outlined)
DrawingFont(FontID(#font_large))
DrawText(0,0,"PB",RGB($FF,$00,$00))
Box(0,0,64,64,RGB($00,$00,$FF))
LineXY( 0,0,64,64,RGB($00,$00,$FF))
LineXY(64,0, 0,64,RGB($00,$00,$FF))
StopDrawing()
handleError(CreateSprite(#spr_mousePointer, 8, 8, #PB_Sprite_AlphaBlending), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_mousePointer)), "Can't draw on sprite.")
FrontColor(RGB($00,$FF,$FF))
LineXY(0,0, OutputWidth() - 1,OutputHeight() - 1)
LineXY(0,0, 0,OutputHeight() - 3)
LineXY(0,0, OutputWidth() - 3,0)
StopDrawing()
TransparentSpriteColorOK(#spr_mousePointer, RGB($00,$00,$00))
handleError(CreateSprite(#spr_corner, 8, 8, #PB_Sprite_AlphaBlending), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_corner)), "Can't draw on sprite.")
Box(0, 0, OutputWidth(), OutputHeight(), RGB($00, $FF, $00))
StopDrawing()
TransparentSpriteColorOK(#spr_corner, RGB($00,$00,$00))
DataSection
cornerCoordinates:
Data.i 0,0, 64,0, 64,64, 0,64
textDescriptions:
Data.s "Standard","Rotate around center", "Rotate off center"
Data.s "Horizontal zoom","Vertical zoom"
Data.s "Horizontal iso squish (zoom) + rotate","Vertical iso squish (zoom) + rotate"
Data.s "Horizontal iso squish + overall zoom + rotate","Vertical iso squish + overall zoom + rotate"
Data.s "Horizontal variable iso squish + rotate","Vertical variable iso squish + rotate"
Data.s "Custom sprite transformation, drag numbered corners to transform"
EndDataSection
Dim corner.Point(3)
Define customSpritePosX = 500, customSpritePosY = 425, i
Restore cornerCoordinates
For i = 0 To 3
handleError(CreateSprite(#spr_text_1 + i, 8, 16, #PB_Sprite_AlphaBlending), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_text_1 + i)), "Can't draw on sprite.")
DrawingMode(#PB_2DDrawing_Outlined)
DrawingFont(FontID(#font_small))
DrawText(0, 0, Str(i + 1), RGB($FF,$FF,$FF), RGB($00,$00,$00))
StopDrawing()
TransparentSpriteColorOK(#spr_text_1 + i, RGB($00,$00,$00))
Read.i corner(i)\x: corner(i)\x + customSpritePosX
Read.i corner(i)\y: corner(i)\y + customSpritePosY
Next
Define tmp$
Restore textDescriptions
For i = 0 To 11
Read.s tmp$
handleError(CreateSprite(#spr_text_descript_1 + i, 6 * Len(tmp$), 16, #PB_Sprite_AlphaBlending), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_text_descript_1 + i)), "Can't draw on sprite.")
DrawingMode(#PB_2DDrawing_Outlined)
DrawingFont(FontID(#font_small))
DrawText(0, 0, tmp$, RGB($FF,$FF,$FF), RGB($00,$00,$00))
StopDrawing()
TransparentSpriteColorOK(#spr_text_descript_1 + i, RGB($00,$00,$00))
Next
Define direction = 2
Define pb_sprite_size = SpriteHeight(#spr_pb)
Define zoomX = SpriteWidth(#spr_pb) ;initial zoom sizes
Define zoomY = SpriteHeight(#spr_pb)
Define angle, newWidth, newHeight, displayRow, event, i
ReleaseMouse(1)
Define mouseIsReleased = #True, dragCorner = -1
Repeat
Repeat
event = WindowEvent()
Select event
Case #PB_Event_CloseWindow
End
EndSelect
Until event = 0
FlipBuffers()
ClearScreen(RGB($40, $40, $40))
displayRow = 70
MakeSpriteNormal(#spr_pb, 32, 32) ;normal appearance
DisplaySprite(#spr_pb, 100, displayRow) ;location is needed because display location was not part of transformation
DisplayTransparentSprite(#spr_text_descript_1, 100 - (pb_sprite_size + SpriteWidth(#spr_text_descript_1)) / 4, displayRow + pb_sprite_size * 0.75)
MakeSpriteRotate(#spr_pb, 32, 32, angle) ;rotate around center
DisplaySprite(#spr_pb, 200, displayRow)
DisplayTransparentSprite(#spr_text_descript_2, 200 - (pb_sprite_size + SpriteWidth(#spr_text_descript_2)) / 4, displayRow + pb_sprite_size * 0.75)
MakeSpriteRotate(#spr_pb, 16, 16, angle) ;rotate around non-center point
DisplaySprite(#spr_pb, 350, displayRow)
DisplayTransparentSprite(#spr_text_descript_3, 350 - (pb_sprite_size + SpriteWidth(#spr_text_descript_3)) / 4, displayRow + pb_sprite_size * 0.75)
newWidth = zoomX: newHeight = zoomX
MakeSpriteZoomHorizontal(#spr_pb, 32, 32, newWidth) ;zoom horizontally
DisplaySprite(#spr_pb, 500, displayRow)
DisplayTransparentSprite(#spr_text_descript_4, 500 - (pb_sprite_size + SpriteWidth(#spr_text_descript_4)) / 4, displayRow + pb_sprite_size * 0.75)
MakeSpriteZoomVertical(#spr_pb, 32, 32, newHeight) ;zoom vertically
DisplaySprite(#spr_pb, 600, displayRow)
DisplayTransparentSprite(#spr_text_descript_5, 600 - (pb_sprite_size + SpriteWidth(#spr_text_descript_5)) / 4, displayRow + pb_sprite_size * 0.75)
displayRow = 170
MakeSpriteIsoCounterClockwise(#spr_pb, 64, 64, 32, 32, angle, 64, 32) ;squished vertically
DisplaySprite(#spr_pb, 150, displayRow)
DisplayTransparentSprite(#spr_text_descript_6, 150 - (pb_sprite_size + SpriteWidth(#spr_text_descript_6)) / 4, displayRow + pb_sprite_size * 0.50)
displayRow = 260
MakeSpriteIsoCounterClockwise(#spr_pb, 64, 64, 32, 32, angle, 32, 64) ;squished horizontally
DisplaySprite(#spr_pb, 150, displayRow)
DisplayTransparentSprite(#spr_text_descript_7, 150 - (pb_sprite_size + SpriteWidth(#spr_text_descript_7)) / 4, displayRow + pb_sprite_size * 0.75)
displayRow = 280
MakeSpriteIsoClockwise(#spr_pb, 64, 64, 32, 32, angle, 2 * zoomX, 1 * zoomX) ;squished vertically and zoomed
DisplaySprite(#spr_pb, 675 - zoomY * 0.5, displayRow - zoomY * 0.5)
DisplayTransparentSprite(#spr_text_descript_8, 650 - (pb_sprite_size + SpriteWidth(#spr_text_descript_8)) / 3, displayRow + pb_sprite_size * 0.75)
MakeSpriteIsoClockwise(#spr_pb, 64, 64, 32, 32, angle, 1 * zoomX, 2 * zoomX) ;squished horizontally and zoomed
DisplaySprite(#spr_pb, 425 - zoomY * 0.5, displayRow - zoomY * 0.5)
DisplayTransparentSprite(#spr_text_descript_9, 400 - (pb_sprite_size + SpriteWidth(#spr_text_descript_9)) / 3, displayRow + pb_sprite_size * 0.75)
displayRow = 380
MakeSpriteIsoCounterClockwise(#spr_pb, 64, 64, 32, 32, angle, 64, 20 + zoomX / 2) ;squishiness varying vertically
DisplaySprite(#spr_pb, 150, displayRow)
DisplayTransparentSprite(#spr_text_descript_10, 150 - (pb_sprite_size + SpriteWidth(#spr_text_descript_10)) / 4, displayRow + pb_sprite_size * 0.75)
displayRow = 490
MakeSpriteIsoCounterClockwise(#spr_pb, 64, 64, 32, 32, angle, 20 + zoomX / 2, 64) ;squishiness varying horizontally
DisplaySprite(#spr_pb, 150, displayRow)
DisplayTransparentSprite(#spr_text_descript_11, 150 - (pb_sprite_size + SpriteWidth(#spr_text_descript_11)) / 4, displayRow + pb_sprite_size * 0.75)
ExamineMouse()
;handle screen mouse activation/deactivation
If MouseX() < 2 Or MouseX() > ScreenWidth() - 3 Or MouseY() < 2 Or MouseY() > ScreenHeight() -3
ReleaseMouse(1): mouseIsReleased = #True
EndIf
If WindowMouseX(#win_main) >= 2 And
WindowMouseX(#win_main) < WindowWidth(#win_main, #PB_Window_InnerCoordinate) - 2 And
WindowMouseY(#win_main) >= 2 And
WindowMouseY(#win_main) < WindowHeight(#win_main, #PB_Window_InnerCoordinate) - 2
ReleaseMouse(0): mouseIsReleased = #False
MouseLocate(WindowMouseX(#win_main), WindowMouseY(#win_main))
EndIf
;handle corner dragging for deformable sprite
If Not mouseIsReleased
If MouseButton(#PB_MouseButton_Left)
If dragCorner = -1
For i = 0 To 3
If SpriteCollision(#spr_corner, corner(i)\x, corner(i)\y, #spr_mousePointer, MouseX(), MouseY())
dragCorner = i
MouseDeltaX(): MouseDeltaY() ;reset these values for further reads
Break
EndIf
Next
Else
corner(dragCorner)\x + MouseDeltaX(): corner(dragCorner)\y + MouseDeltaY()
EndIf
ElseIf dragCorner <> -1
corner(dragCorner)\x + MouseDeltaX(): corner(dragCorner)\y + MouseDeltaY()
dragCorner = -1 ;signal that no corner is being dragged
EndIf
EndIf
TransformSprite2D(#spr_pb, corner(0)\x,corner(0)\y, corner(1)\x,corner(1)\y, corner(2)\x,corner(2)\y, corner(3)\x,corner(3)\y)
DisplaySprite(#spr_pb, 0, 0) ;drawn at location 0,0 because sprite location is part of transformation
DisplayTransparentSprite(#spr_text_descript_12, customSpritePosX - (pb_sprite_size + SpriteWidth(#spr_text_descript_12)) / 4, customSpritePosY + pb_sprite_size * 1.5, 180)
;display corners for dragging
For i = 0 To 3
DisplayTransparentSprite(#spr_corner, corner(i)\x, corner(i)\y, 128)
DisplayTransparentSprite(#spr_text_1 + i, corner(i)\x + 8, corner(i)\y)
Next
If Not mouseIsReleased: DisplayTransparentSprite(#spr_mousePointer, MouseX(), MouseY()): EndIf
;update animation attributes
angle + 1: If angle > 360: angle = 0: EndIf
zoomX + direction
If zoomX > 100 : direction = -2 : EndIf
If zoomX < 20 : direction = 2 : EndIf
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape): Break: EndIf
Delay(1)
ForEver
@Edit2: Made a cosmetic change by renaming the constant #pb_subsystem to #pb_sprite_subsystem.