Dieses Thema soll als Diskussionsort für ein Problem dienen, welches wir kurz in der Shoutbox mit UEZ erörtert haben.
Auch wenn du ( UEZ) paar Tage nichts von mir gehört hast, war ich nicht untätig.
Ich habe mir brav FreeBasic angesehen.
Da aber sowohl die Sprache, als auch die Thematik, für mich Neuland waren, musste ich ein eigenes Programm von Grundauf erstellen um die Sache zu verstehen, anstatt am vorhandenen Skript rumzudoktorn.
Kurzum: Irgendwann habe ich aber entnervt aufgegeben - FB und ich werden wohl keine Freunde mehr.
Anstatt mich nun noch mit einer anderen Sprache rumzuschlagen habe ich mir für das Thema good old AutoIt wieder hochgeholt und versucht damit nun zu zeigen wie ich mir das algorithmisch vorstelle, was ich in der Shoutbox beschrieben habe.
Das ganze hat dann aber auch länger gebraucht, da ich absolut Null Plan von GDI+ hatte.
Am Ende habe ich aber nun etwas vorzeigbares erstellt was evtl. als Grundlage für weitere Diskussionen und Verbesserungen dienen kann.
Im Idealfall sind hieran die Mathematik dahinter schon erkennbar, so dass du bestimmte Dinge in dein FB-Programm übernehmen kannst:
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
OnAutoItExitRegister(_exit)
Opt("GUIOnEventMode", 1)
Global Const $PI = ACos(-1), $PI_2 = ASin(1), $PI_4 = ATan(1), $_2PI = $PI + $PI
main()
Func main()
Local $mC = _b3d_createCanvas(1000, 750)
; start moving textured ball
Local $mTexture = _b3d_importTexture(@ScriptDir & "\13h_310x156.bmp")
_b3d_ballMoving($mC, 50, 50, 50, 5, 3, $mTexture)
; GUI endless loop
While Sleep(100)
WEnd
EndFunc
; demo function for drawing a moving ball
Func _b3d_ballMoving(ByRef $mCanvas, Const $iRadius, Const $iXStart, Const $iYStart, Const $fVx, Const $fVy, $mTexture, Const $fRotX0 = 0, Const $fRotY0 = 0)
Local $iXPos = $iXStart, $iYPos = $iYStart
Local $fRotX = $fRotX0, $fRotY = $fRotY0
Local $fU = 2 * $PI * $iRadius
Do
; clear old image
_GDIPlus_GraphicsClear($mCanvas.Context, 0xFF303030)
; draw textured sphere
_b3d_drawTexturedSphere($mCanvas, $iXPos, $iYPos, $iRadius, $mTexture, $fRotX, $fRotY)
_b3d_canvasShow($mCanvas)
$iXPos += $fVx
$iYPos += $fVy
$fRotX += __b3d_modPos($fVy, $fU) / $fU * $_2PI
$fRotY -= __b3d_modPos($fVx, $fU) / $fU * $_2PI
Until $iXPos > 900
EndFunc
; draws a sphere and map a texture on it
Func _b3d_drawTexturedSphere(ByRef $mCanvas, $iMx, $iMy, $iR, $mTexture, $iRotX = 0, $iRotY = 0)
Local $iTexWidth = $mTexture.width
Local $iTexHeight = $mTexture.height
Local $iX, $iY, $iHalfChord, $iSegmentHeight
Local $fXTex, $fYTex
Local $iColor
Local $aBitmap = $mTexture.Pixels ; faster than _GDIPlus_BitmapGetPixel
Local $hBitmap = $mCanvas.Bitmap
; Koordinaten-Transformation als LUT um Doppelberechnung zu vermeiden
; Zum jeweiligen aktuellen Kreissegment (Radius - aktuelle Position im Kreis) wird die dazugehörige Koordinate im Texturraum berechnet
Local $aTexCoordsY[$iR + 1][2]
Local $aTexCoordsX[$iR + $iR + 1]
Local $fRad, $fRadX, $fRadY, $fRadYBottom
For $iH = 0 To $iR
; der Kreisbogenabstand vom Zenitpunkt (=Kugelwinkel in rad)
$fRad = ASin(($iR-$iH) / $iR)
; upper half
$fRadY = $fRad + $iRotX
$aTexCoordsY[$iH][0] = Round(__b3d_modPos($PI_2 - $fRadY, $Pi) / $Pi * ($iTexHeight - 1))
; lower half
$fRadYBottom = $fRadY - $fRad - $fRad
$aTexCoordsY[$iH][1] = Round(__b3d_modPos($PI_2 - $fRadYBottom, $Pi) / $Pi * ($iTexHeight - 1))
; left half
$fRadX = $PI - $fRad + $iRotY ; "+ $PI": um 0-Punkt auf Texturmitte zu platzieren
$aTexCoordsX[$iH] = Round(__b3d_modPos($fRadX , $_2PI) / $_2PI * ($iTexWidth - 1))
; right half
$fRadXRight = $fRad + $PI + $iRotY ; "+ $PI": um 0-Punkt auf Texturmitte zu platzieren
$aTexCoordsX[$iR + $iR - $iH] = Round(__b3d_modPos($fRadXRight, $_2PI) / $_2PI * ($iTexWidth - 1))
Next
; middle line (extra to avoid double processing):
$iYTexMid = $aTexCoordsY[$iR][0]
For $iX = $iMx - $iR To $iMx + $iR
$fXTex = $aTexCoordsX[$iX - $iMx + $iR]
$iColor = $aBitmap[$fXTex][$iYTexMid]
DllCall($__g_hGDIPDll, "int", "GdipBitmapSetPixel", "handle", $hBitmap, "int", $iX, "int", $iMy, "uint", $iColor) ; faster than extra _GDIPlus_BitmapSetPixel()
Next
; rest of circle area
For $iSegmentHeight = 0 To $iR - 1
; halbe Sehnenlänge
$iHalfChord = Int(Round(Sqrt(2*$iR*$iSegmentHeight - ($iSegmentHeight*$iSegmentHeight))))
; Koordinaten des Quellpixels aus dem Texturbitmap
$fYTex = $aTexCoordsY[$iSegmentHeight][0]
$fYTexBottom = $aTexCoordsY[$iSegmentHeight][1]
For $iX = $iMx - $iHalfChord To $iMx + $iHalfChord
$fXTex = $aTexCoordsX[$iX - $iMx + $iR]
; draw top case
$iColor = $aBitmap[$fXTex][$fYTex]
$iY = $iMy + $iSegmentHeight - $iR
DllCall($__g_hGDIPDll, "int", "GdipBitmapSetPixel", "handle", $hBitmap, "int", $iX, "int", $iY, "uint", $iColor)
; draw bottom case
$iColor = $aBitmap[$fXTex][$fYTexBottom]
$iY = $iMy - $iSegmentHeight + $iR
DllCall($__g_hGDIPDll, "int", "GdipBitmapSetPixel", "handle", $hBitmap, "int", $iX, "int", $iY, "uint", $iColor)
Next
Next
EndFunc
Func _b3d_importTexture($sPath)
Local $mTexture[]
Local $hBitmap = _GDIPlus_BitmapCreateFromFile($sPath)
$mTexture.Bitmap = $hBitmap
If @error Then Return SetError(@error, @extended, Null)
$mTexture.width = _GDIPlus_ImageGetWidth($hBitmap)
$mTexture.height = _GDIPlus_ImageGetHeight($hBitmap)
; convert to AutoIt-Array for faster pixel access
Local $aBitmap[$mTexture.width][$mTexture.height], $x, $y
For $x = 0 To $mTexture.width - 1
For $y = 0 To $mTexture.height - 1
$aBitmap[$x][$y] = _GDIPlus_BitmapGetPixel($hBitmap, $x, $y)
Next
Next
$mTexture.Pixels = $aBitmap
Return $mTexture
EndFunc
; function to draw a filled circle on a bitmap
Func _b3d_drawCircleFilled(ByRef $mCanvas, $iMx, $iMy, $iR, $sColor = "0xFFFFFFFF")
Local $iX, $iY, $iHalfChord, $iSegmentHeight
; middle line (extra to avoid double processing):
For $iX = $iMx - $iR To $iMx + $iR
_GDIPlus_BitmapSetPixel($mCanvas.Bitmap, $iX, $iMy, $sColor)
Next
; rest of circle area
For $iSegmentHeight = 0 To $iR - 1
$iHalfChord = Round(Sqrt(2*$iR*$iSegmentHeight - ($iSegmentHeight*$iSegmentHeight)))
For $iX = $iMx - $iHalfChord To $iMx + $iHalfChord
$iY = $iMy + $iSegmentHeight - $iR
_GDIPlus_BitmapSetPixel($mCanvas.Bitmap, $iX, $iY, $sColor)
$iY = $iMy - $iSegmentHeight + $iR
_GDIPlus_BitmapSetPixel($mCanvas.Bitmap, $iX, $iY, $sColor)
Next
Next
EndFunc
; zeigt den aktuellen Zustand der Canvas (des Zeichnungsbitmaps an)
Func _b3d_canvasShow(ByRef $mCanvas)
_GDIPlus_GraphicsDrawImage($mCanvas.Graphic, $mCanvas.Bitmap, 0, 0)
EndFunc
Func _b3d_createCanvas($iWidth, $iHeight)
; map which holds all the necessary parameters
Local $mCanvas[]
$mCanvas.width = $iWidth
$mCanvas.height = $iHeight
; Initialize GDI+ library
_GDIPlus_Startup()
; create GUI
$mCanvas.GUI = GUICreate("Ball 3D", $iWidth, $iHeight)
GUISetBkColor(0x303030, $mCanvas.GUI) ;set GUI background color
GUISetOnEvent($GUI_EVENT_CLOSE, _exit)
GUISetState(@SW_SHOW, $mCanvas.GUI)
; create Canvas
$mCanvas.Graphic = _GDIPlus_GraphicsCreateFromHWND($mCanvas.GUI)
$mCanvas.Bitmap = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $mCanvas.Graphic)
$mCanvas.Context = _GDIPlus_ImageGetGraphicsContext($mCanvas.Bitmap)
Return $mCanvas
EndFunc
; positive modulo
Func __b3d_modPos($divident, $divisor)
Local $m = Mod($divident, $divisor)
Return $m < 0 ? $m + $divisor : $m
EndFunc
Func _exit()
_GDIPlus_Shutdown()
Exit
EndFunc
Alles anzeigen
Die Texturdatei liegt im Anhang.