[BlitzMax] Pseudo-Dynamisches Schadensmodell 2D (ddm2d_v098)

Übersicht Sonstiges Projekte

Neue Antwort erstellen

 

CO2

ehemals "SirMO"

Betreff: [BlitzMax] Pseudo-Dynamisches Schadensmodell 2D (ddm2d_v098)

BeitragSo, Feb 01, 2015 17:01
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo,

ich möchte hier kurz vorstellen, woran ich die letzten Tage gearbeitet habe, denn ich denke, es ist durchaus ganz ansehnlich geworden. Zudem könnte es den einen oder anderen interessieren.
Die dynamische Schadensmodell-Berechnung hat mich doch nicht in Ruhe gelassen - ich habe auch in diesem Forum den ein oder anderen Beitrag mit Fragen dazu gepostet. Mir schwebte schon etwas länger eine Idee im Kopf, wie man es zumindest halbwegs realistisch für zwei Dimensionen umsetzen könnte. Dennoch ist es nur eine Pseudo-dynamische Berechnung, denn physikalische Faktoren wie Material oder Stabilität fließen entweder gar nicht oder nur bedingt in die Berechnung mit ein.
Funktionsweise: Die 2D-"Modelle" werden in Würfel eingeteilt, je kleiner die Würfel, desto detaillierter später das Schadensmodell. Jeder dieser Würfel besitzt einen HP-Status - ist dieser auf 0, so wird der Würfel (das sog. Quad) nicht mehr gezeichnet. Dies bildet die Basis (natürlich besitzt das Quad an sich auch noch Verschönerungs-Funktionen (z.B. einen Welleneffekt, etc.) - Aber prinzipiell läuft es so. Aus diesen Quads besteht dann das Modell intern. Nun können Aufprälle simuliert werden, alles was dafür zu tun ist, ist anzugeben, von welcher Himmelsrichtung (relativ gesehen) der Aufprall kam, wie stark er war und in welchem Bereich er auftrat. Aus diesen Parametern werden dann die betreffenden Quads angepasst und fertig ist das berechnete Schadensmodell, mit dem dann weitergerechnet werden kann.
Das alles funktioniert soweit auch, es gibt allerdings noch ein paar Anomalien, weswegen es sich nur um Version 0.98 handelt. Zudem ist das RAM-Management glaube ich nicht das gelbe vom Ei - hier bin ich für Vorschläge offen. Womit ich auch noch nicht zufrieden bin ist die relativ hohe Prozessorauslastung bei wenigen Modellen. Dies könnte an den Pixmaps liegen, die ich verwende, ein besserer Weg fällt mir aber leider nicht ein.
So, jetzt aber genug geredet, hier mal ein paar Bilder (links das "normale" Modell):
user posted image

Und der Quellcode (des Includes):
BlitzMax: [AUSKLAPPEN]
Rem
Autor: Marius Otto
Datum: 28.01.2015
Beschr.: DDM2D - Dynamic Damage Model in 2 Dimensions
Jedes DDMObjekt wird in Quads unterteilt. Diese Quads bestehen aus Einzelbildern, die das Modell repräsentieren.
End Rem


SuperStrict

SeedRnd(MilliSecs())

Private
Function BitIsSet:Byte(Data:Byte, Bits:Byte)
If (Data & Bits = Bits)
Return True
Else
Return False
EndIf
End Function

Public
' ### TDDMQuad ####################################################################################
Type TDDMQuad
' ### FLAGS #############################################################
Const FLAG_NONE:Byte = 0 ' Kein Flag gesetzt
Const FLAG_ISENGINE:Byte = 1 ' Es handelt sich bei dem Quad um ein EngineQuad, also um ein Quad, das den Motor repräsentiert (für erweiterte "Simulationen")
Const FLAG_ISWHEEL:Byte = 2 ' Es handelt sich bei dem Quad um ein WheelQuad, also um ein Quad, das einen Reifen repräsentiert (für erweiterte "Simulationen")
Const FLAG_GODQUAD:Byte = 4 ' Es handelt sich um ein unzerstörbares Quad
Const FLAG_ISBORDER:Byte = 8 ' Systemflag, nicht benutzen.
Const FLAG_ISEDGE:Byte = 16 ' Systemflag, nicht benutzen.
Const FLAG_ISCOLORED:Byte = 32 ' Bei dem Quad handelt es sich nicht um ein Image-Quad
Const FLAG_DEFORMEDL:Byte = 64 ' Systemflag, nicht benutzen.
Const FLAG_DEFORMEDR:Byte = 128 ' Systemflag, nicht benutzen.

' ### Deform-Align ######################################################
Const DEFORM_RIGHT:Int = 0
Const DEFORM_LEFT:Int = 1

' ### Deform-Direction ##################################################
Const DEFORM_NORTH:Int = 0
Const DEFORM_EAST:Int = 1
Const DEFORM_SOUTH:Int = 2
Const DEFORM_WEST:Int = 3

' ### Collapse-Direction ################################################
Const COLLAPSE_NORTH:Int = 0
Const COLLAPSE_EAST:Int = 1
Const COLLAPSE_SOUTH:Int = 2
Const COLLAPSE_WEST:Int = 3

' ### MaxHealth #########################################################
Const QUAD_MAXHEALTH:Double = 100

' ### Variablen #########################################################
Field ImageData:TImage

Field ColorDataRed:Int
Field ColorDataGreen:Int
Field ColorDataBlue:Int

Field Health:Double ' In Prozent. Bei <= 0 Prozent wird das Quad nicht mehr gezeichnet
Field FLAGS:Byte ' Flags: siehe oben
Field QuadSize:Byte

' ### Konstruktion ######################################################
Function CreateImgQuad:TDDMQuad(ImageSrc:TImage, Health:Double = 100, Flags:Byte = FLAG_NONE, QuadSize:Int = 10)
Local ReturnMe:TDDMQuad = New TDDMQuad

ReturnMe.ImageData = ImageSrc

If (Health > QUAD_MAXHEALTH)
ReturnMe.Health = QUAD_MAXHEALTH
Else
ReturnMe.Health = Health
EndIf
ReturnMe.FLAGS = Flags
ReturnMe.QuadSize = QuadSize

Return ReturnMe
End Function

Function CreateColQuad:TDDMQuad(ColorRed:Int, ColorGreen:Int, ColorBlue:Int, Health:Double = 100, Flags:Byte = FLAG_ISCOLORED, QuadSize:Int = 10)
Local ReturnMe:TDDMQuad = New TDDMQuad

ReturnMe.ColorDataRed = ColorRed
ReturnMe.ColorDataGreen = ColorGreen
ReturnMe.ColorDataBlue = ColorBlue

ReturnMe.Health = Health
ReturnMe.FLAGS = Flags
ReturnMe.QuadSize = QuadSize

Return ReturnMe
End Function

' ### Methoden ##########################################################
Method SetHealth(Health:Double)
Self.Health = Health
End Method

Method GetHealth:Double()
Return Self.Health
End Method

Method DrawQuad(PositionX:Double, PositionY:Double)
Local prevred:Int
Local prevgreen:Int
Local prevblue:Int

If (Self.Health > 0 Or BitIsSet(Self.FLAGS, FLAG_GODQUAD))
GetColor(prevred, prevgreen, prevblue)

If (BitIsSet(Self.FLAGS, FLAG_ISCOLORED))
SetColor(Self.ColorDataRed, Self.ColorDataGreen, Self.ColorDataBlue)
DrawRect(PositionX, PositionY, Self.QuadSize, Self.QuadSize)
Else
If (Self.ImageData <> Null)
SetColor(255, 255, 255)
DrawImage(Self.ImageData, PositionX, PositionY)
EndIf
EndIf
EndIf

SetColor(prevred, prevgreen, prevblue)
End Method

Method Dent(Direction:Int, InvisibleARGB:Int = $FF000000)
Local TakeMe:TImage = CreateImage(Self.QuadSize, Self.QuadSize)
Local takemepix:TPixmap = LockImage(TakeMe)
Local temppix:TPixmap = LockImage(Self.ImageData)

For Local y:Int = 0 To (takemepix.height - 1)
For Local x:Int = 0 To (takemepix.width - 1)
WritePixel(takemepix, x, y, InvisibleARGB)
Next
Next

Local diff:Int = 0 ' Maxdiff: Quadsize / 4
Local maxdiff:Int = (Self.QuadSize / 4)
Local option:Int

Local x:Int, y:Int

Select(Direction)
Case COLLAPSE_NORTH
For x = 0 To (Self.QuadSize - 1)
option = Rand(0, 1)

If (option = 0) ' Addieren
diff = diff + 1

If (diff > maxdiff) Then diff = maxdiff
Else ' subtrahieren
diff = diff - 1

If (diff < 0) Then diff = 0
EndIf

For y = 0 To (Self.QuadSize - 1)
If (y + diff < Self.QuadSize And y + diff >= 0)
WritePixel(takemepix, x, (y + diff), ReadPixel(temppix, x, y))
Else
WritePixel(takemepix, x, y, ReadPixel(temppix, x, y))
EndIf
Next
Next
Case COLLAPSE_EAST
For y = 0 To (Self.QuadSize - 1)
option = Rand(0, 1)

If (option = 0) ' Addieren
diff = diff - 1

If (diff < -maxdiff) Then diff = -maxdiff
Else ' subtrahieren
diff = diff + 1

If (diff > 0) Then diff = 0
EndIf

For x = 0 To (Self.QuadSize - 1)
If (x + diff < Self.QuadSize And x + diff >= 0)
WritePixel(takemepix, (x + diff), y, ReadPixel(temppix, x, y))
Else
WritePixel(takemepix, x, y, ReadPixel(temppix, x, y))
EndIf
Next
Next
Case COLLAPSE_SOUTH
For x = 0 To (Self.QuadSize - 1)
option = Rand(0, 1)

If (option = 0) ' Subtrahieren
diff = diff - 1

If (diff < -maxdiff) Then diff = -maxdiff
Else ' Addieren
diff = diff + 1

If (diff > 0) Then diff = 0
EndIf

For y = 0 To (Self.QuadSize - 1)
If (y + diff < Self.QuadSize And y + diff >= 0)
WritePixel(takemepix, x, (y + diff), ReadPixel(temppix, x, y))
Else
WritePixel(takemepix, x, y, ReadPixel(temppix, x, y))
EndIf
Next
Next
Case COLLAPSE_WEST
For y = 0 To (Self.QuadSize - 1)
option = Rand(0, 1)

If (option = 0) ' Addieren
diff = diff + 1

If (diff > maxdiff) Then diff = maxdiff
Else ' subtrahieren
diff = diff - 1

If (diff < 0) Then diff = 0
EndIf

For x = 0 To (Self.QuadSize - 1)
If (x + diff < Self.QuadSize And x + diff >= 0)
WritePixel(takemepix, (x + diff), y, ReadPixel(temppix, x, y))
Else
WritePixel(takemepix, x, y, ReadPixel(temppix, x, y))
EndIf
Next
Next
End Select

UnlockImage(Self.ImageData)
UnlockImage(TakeMe)

Self.ImageData = TakeMe
End Method

Method Deform(Align:Int, Direction:Int, NextQuad:TDDMQuad, InvisibleARGB:Int = $FF000000)
If ((BitIsSet(Self.FLAGS, FLAG_ISEDGE) Or BitIsSet(Self.FLAGS, FLAG_ISBORDER)) And Not BitIsSet(Self.FLAGS, FLAG_ISCOLORED) And NextQuad <> Null)
If (BitIsSet(Self.FLAGS, FLAG_DEFORMEDL) Or BitIsSet(Self.FLAGS, FLAG_DEFORMEDR))

Else ' Deformieren
Local ThisPixmap:TPixmap
Local OtherPixmap:TPixmap

ThisPixmap = LockImage(Self.ImageData)
OtherPixmap = LockImage(NextQuad.ImageData)

Local ResImage:TImage
Local ResPixmap:TPixmap

If (Direction = DEFORM_NORTH Or Direction = DEFORM_SOUTH)
ResImage = CreateImage(Self.QuadSize, (Self.QuadSize + NextQuad.QuadSize))
ElseIf(Direction = DEFORM_EAST Or Direction = DEFORM_WEST)
ResImage = CreateImage((Self.QuadSize + NextQuad.QuadSize), Self.QuadSize)
EndIf

ResPixmap = LockImage(ResImage)

If (ResPixmap <> Null)
Local Offset:Int, x:Int, y:Int, color:Int

' das gesamte Bild durchsichtig:
For y = 0 To (ResPixmap.Height - 1)
For x = 0 To (ResPixmap.Width - 1)
WritePixel(ResPixmap, x, y, InvisibleARGB)
Next
Next

Select(Direction)
Case DEFORM_NORTH
If (Align = DEFORM_LEFT)
Offset = (ThisPixmap.Height - 1)
Else
Offset = 0
EndIf
' 1. Kiste übernehmen:
For x = 0 To (ThisPixmap.Width - 1)
For y = 0 To (ThisPixmap.Height - 1)
WritePixel(ResPixmap, x, (y + Offset), ReadPixel(ThisPixmap, x, y))
Next

If (Align = DEFORM_LEFT)
Offset = Offset - 1
Else
Offset = Offset + 1
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = ThisPixmap.Height + (OtherPixmap.Height - 1)
Else
Offset = ThisPixmap.Height
EndIf
' 2. Kiste:
For x = 0 To (OtherPixmap.Width - 1)
For y = 0 To (OtherPixmap.Height - 1)
If (y + Offset < ResPixmap.Height)
WritePixel(ResPixmap, x, (y + Offset), ReadPixel(OtherPixmap, x, y))
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = Offset - 1
Else
Offset = Offset + 1
EndIf
Next
' Alles wieder retour:
For y = 0 To (Self.QuadSize - 1)
For x = 0 To (Self.QuadSize - 1)
WritePixel(ThisPixmap, x, y, ReadPixel(ResPixmap, x, y))
Next
Next
For y = 0 To (Self.QuadSize - 1)
For x = 0 To (Self.QuadSize - 1)
WritePixel(OtherPixmap, x, y, ReadPixel(ResPixmap, x, (y + Self.QuadSize)))
Next
Next
Case DEFORM_EAST
If (Align = DEFORM_LEFT)
Offset = 0
Else
Offset = (ThisPixmap.Width - 1)
EndIf
' 1. Kiste übernehmen:
For y = 0 To (ThisPixmap.Height - 1)
For x = 0 To (ResPixmap.Width - 1)
If (x < ThisPixmap.Width)
WritePixel(ResPixmap, (x + Offset), y, ReadPixel(ThisPixmap, x, y))
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = Offset + 1
Else
Offset = Offset - 1
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = (OtherPixmap.Width - 1)
Else
Offset = 0
EndIf
' 2. Kiste übernehmen:
For y = 0 To (OtherPixmap.Height - 1)
For x = 0 To (OtherPixmap.Width - 1)
If ((x - Offset) >= 0)
WritePixel(ResPixmap, (x - Offset), y, ReadPixel(OtherPixmap, x, y))
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = Offset - 1
Else
Offset = Offset + 1
EndIf
Next
' Alles wieder retour:
For y = 0 To (Self.QuadSize - 1)
For x = 0 To (Self.QuadSize - 1)
WritePixel(OtherPixmap, x, y, ReadPixel(ResPixmap, x, y))
Next
Next
For y = 0 To (Self.QuadSize - 1)
For x = 0 To (Self.QuadSize - 1)
WritePixel(ThisPixmap, x, y, ReadPixel(ResPixmap, (x + Self.QuadSize), y))
Next
Next
Case DEFORM_SOUTH
If (Align = DEFORM_LEFT)
Offset = 0
Else
Offset = (ThisPixmap.Height - 1)
EndIf
' 1. Kiste übernehmen:
For x = 0 To (ThisPixmap.Width - 1)
For y = 0 To (ThisPixmap.Height - 1)
WritePixel(ResPixmap, x, (y + Offset), ReadPixel(ThisPixmap, x, y))
Next

If (Align = DEFORM_LEFT)
Offset = Offset + 1
Else
Offset = Offset - 1
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = (ThisPixmap.Height - 1)
Else
Offset = 0
EndIf
' 2. Kiste:
For x = 0 To (OtherPixmap.Width - 1)
For y = 0 To (OtherPixmap.Height - 1)
If ((y - Offset) >= 0)
WritePixel(ResPixmap, x, (y - Offset), ReadPixel(OtherPixmap, x, y))
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = Offset - 1
Else
Offset = Offset + 1
EndIf
Next
' Alles wieder retour:
For y = 0 To (Self.QuadSize - 1)
For x = 0 To (Self.QuadSize - 1)
WritePixel(OtherPixmap, x, y, ReadPixel(ResPixmap, x, y))
Next
Next
For y = 0 To (Self.QuadSize - 1)
For x = 0 To (Self.QuadSize - 1)
WritePixel(ThisPixmap, x, y, ReadPixel(ResPixmap, x, (y + Self.QuadSize)))
Next
Next
Case DEFORM_WEST
If (Align = DEFORM_LEFT)
Offset = 0
Else
Offset = (ThisPixmap.Width - 1)
EndIf
' 1. Kiste übernehmen:
For y = 0 To (ThisPixmap.Height - 1)
For x = 0 To (ResPixmap.Width - 1)
If (x < ThisPixmap.Width)
WritePixel(ResPixmap, (x + Offset), y, ReadPixel(ThisPixmap, x, y))
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = Offset + 1
Else
Offset = Offset - 1
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = (ThisPixmap.Width)
Else
Offset = (ResPixmap.Width - 1)
EndIf
' 2. Kiste übernehmen:
For y = 0 To (OtherPixmap.Height - 1)
For x = 0 To (OtherPixmap.Width - 1)
If ((Offset + x) < ResPixmap.Width)
WritePixel(ResPixmap, (x + Offset), y, ReadPixel(OtherPixmap, x, y))
EndIf
Next

If (Align = DEFORM_LEFT)
Offset = Offset + 1
Else
Offset = Offset - 1
EndIf
Next

' Alles wieder retour:
For y = 0 To (Self.QuadSize - 1)
For x = 0 To (Self.QuadSize - 1)
WritePixel(ThisPixmap, x, y, ReadPixel(ResPixmap, x, y))
Next
Next
For y = 0 To (Self.QuadSize - 1)
For x = 0 To (Self.QuadSize - 1)
WritePixel(OtherPixmap, x, y, ReadPixel(ResPixmap, (x + Self.QuadSize), y))
Next
Next
End Select
EndIf

UnlockImage(NextQuad.ImageData)
UnlockImage(Self.ImageData)
UnlockImage(ResImage)

If (Align = DEFORM_RIGHT)
Self.AddFlag(FLAG_DEFORMEDR)
NextQuad.AddFlag(FLAG_DEFORMEDR)
Else
Self.AddFlag(FLAG_DEFORMEDL)
NextQuad.AddFlag(FLAG_DEFORMEDL)
EndIf

Self.Dent(Direction, InvisibleARGB)
EndIf
EndIf
End Method

Method Collapse(Direction:Int, NextQuad:TDDMQuad, NewHealth:Double = QUAD_MAXHEALTH, InvisibleARGB:Int = $FF000000)
If (NextQuad = Null) Then Return

If (Self.Health > 0)
NextQuad.ImageData = Self.ImageData
NextQuad.Dent(Direction, InvisibleARGB)
EndIf

NextQuad.Health = NewHealth
Self.Health = 0
End Method

Method AddFlag(Flag:Byte)
Self.FLAGS = Self.FLAGS + Flag
End Method
End Type

Function DDModelFromImage:TDDModel(ImageSrc:Object, QuadSize:Int = 10, QuadHP:Double = 100)
Local Image:TImage = LoadImage(ImageSrc)
If (Image = Null) Then Return Null

If (ImageWidth(Image) Mod QuadSize <> 0 Or ImageHeight(Image) Mod QuadSize <> 0) Then Return Null

Local Pixmap:TPixmap = LockImage(Image)

Local tmpix:TPixmap
Local tmpimg:TImage

Local Width:Int = ImageWidth(Image) / QuadSize
Local Height:Int = ImageHeight(Image) / QuadSize

Local Quads:TDDMQuad[Width, Height]

For Local y:Int = 0 To (Height - 1)
For Local x:Int = 0 To (Width - 1)
tmpimg = CreateImage(QuadSize, QuadSize)
tmpix = LockImage(tmpimg)

For Local yy:Int = 0 To (QuadSize - 1)
For Local xx:Int = 0 To (QuadSize - 1)
WritePixel(tmpix, xx, yy, ReadPixel(Pixmap, (x * QuadSize) + xx, (y * QuadSize) + yy))
Next
Next

UnlockImage(tmpimg)

Quads[x, y] = TDDMQuad.CreateImgQuad(tmpimg, QuadHP, TDDMQuad.FLAG_NONE, QuadSize)
Next
Next

UnlockImage(Image)

Return TDDModel.Create(TDDModelQuadData.Create(Quads, Width, Height))
End Function

' ### TDDModelQuadData (Helper-Klasse) ############################################################
Type TDDModelQuadData
Field Data:TDDMQuad[,]
Field Width:Int
Field Height:Int

Function Create:TDDModelQuadData(Data:TDDMQuad[,], Width:Int, Height:Int)
Local ReturnMe:TDDModelQuadData = New TDDModelQuadData

ReturnMe.Data = Data
ReturnMe.Width = Width
ReturnMe.Height = Height

Return ReturnMe
End Function
End Type

' ### TDDModel ####################################################################################
Type TDDModel
' ### Konstanten ########################################################
Const DIRECTION_NORTH:Byte = 0
Const DIRECTION_EAST:Byte = 1
Const DIRECTION_SOUTH:Byte = 2
Const DIRECTION_WEST:Byte = 3

' ### Variablen #########################################################
Field ModelData:TDDModelQuadData
Field PositionX:Int
Field PositionY:Int
Field Rotation:Double

' ### Konstruktion ######################################################
Function Create:TDDModel(ModelData:TDDModelQuadData)
Local ReturnMe:TDDModel = New TDDModel

ReturnMe.ModelData = TDDModelQuadData.Create(ModelData.Data, ModelData.Width, ModelData.Height)

ReturnMe.SyncBorder()

Return ReturnMe
End Function

' ### Methoden ##########################################################
Method SyncBorder() ' Diese Methode wird vom Konstruktor aufgerufen.
If (Self.ModelData <> Null And Self.ModelData.Data <> Null)
For Local y:Int = 0 To (Self.ModelData.Height - 1)
For Local x:Int = 0 To (Self.ModelData.Width - 1)
If (x = 0 Or x = (Self.ModelData.Width - 1) Or y = 0 Or y = (Self.ModelData.Height - 1))
Self.ModelData.Data[x, y].AddFlag(TDDMQuad.FLAG_ISBORDER)
EndIf

If ((x = 0 And y = 0) Or (x = (Self.ModelData.Width - 1) And y = 0) Or (x = 0 And y = (Self.ModelData.Height - 1)) Or (x = (Self.ModelData.Width - 1) And y = (Self.ModelData.Height - 1)))
Self.ModelData.Data[x, y].AddFlag(TDDMQuad.FLAG_ISEDGE)
EndIf
Next
Next
EndIf
End Method

Method SetPosition(PositionX:Int, PositionY:Int)
Self.PositionX = PositionX
Self.PositionY = PositionY
End Method

Method SetRot(Rotation:Double)
Self.Rotation = Rotation
End Method

Method Draw()
Local PrevRot:Double = GetRotation()
SetRotation(Self.Rotation)

Rem
x__COMMENT51__
y__COMMENT52__
End Rem


Local sizex:Int, sizey:Int, ergx:Double, ergy:Double, momQuad:TDDMQuad

For Local y:Int = 0 To (Self.ModelData.Height - 1)
For Local x:Int = 0 To (Self.ModelData.Width - 1)
momQuad = Self.ModelData.Data[x, y]

If (momQuad <> Null)
sizex = momQuad.QuadSize * x
sizey = momQuad.QuadSize * y

ergx = Cos(Self.Rotation) * sizex - Sin(Self.Rotation) * sizey
ergy = Sin(Self.Rotation) * sizex + Cos(Self.Rotation) * sizey

momQuad.DrawQuad(Self.PositionX + ergx, Self.PositionY + ergy)
EndIf
Next
Next

SetRotation(PrevRot)
End Method

Method SimImpact(Direction:Byte, StartQuad:Int, Length:Int, Intensity:Double, Recursive:Byte = True, InvisibleARGB:Int = $FF000000)
If (Direction = DIRECTION_NORTH Or Direction = DIRECTION_SOUTH)
If (StartQuad < 0 Or (StartQuad + Length) > (Self.ModelData.Width - 1))
Return
EndIf
Else
If (StartQuad < 0 Or (StartQuad + Length) > (Self.ModelData.Height - 1))
Return
EndIf
EndIf

If (Intensity <= 0) Then Return

Local x:Int, y:Int

Select(Direction)
Case DIRECTION_NORTH
If (StartQuad >= 1)
For y = 0 To (Self.ModelData.Height - 2)
If (Self.ModelData.Data[(StartQuad - 1), y].Health > 0)
Self.ModelData.Data[(StartQuad - 1), y].Deform(TDDMQuad.DEFORM_RIGHT, TDDMQuad.DEFORM_NORTH, Self.ModelData.Data[(StartQuad - 1), (y + 1)], InvisibleARGB)
Exit
EndIf
Next
EndIf

If ((StartQuad + Length) < (Self.ModelData.Width - 1))
For y = 0 To (Self.ModelData.Height - 2)
If (Self.ModelData.Data[(StartQuad + Length + 1), y].Health > 0)
Self.ModelData.Data[(StartQuad + Length + 1), y].Deform(TDDMQuad.DEFORM_LEFT, TDDMQuad.DEFORM_NORTH, Self.ModelData.Data[(StartQuad + Length + 1), (y + 1)], InvisibleARGB)
Exit
EndIf
Next
EndIf

For x = StartQuad To (StartQuad + Length)
For y = 0 To (Self.ModelData.Height - 2)
If (Self.ModelData.Data[x, y].Health > 0)
If (Intensity > (Self.ModelData.Data[x, y].Health * 100) And Recursive = True)
Intensity = Intensity - (Self.ModelData.Data[x, y].Health * 100)
Self.SimImpact(DIRECTION_NORTH, x, 1, Intensity, True, InvisibleARGB)
Else
If (Recursive = True)
Self.ModelData.Data[x, y].Collapse(TDDMQuad.COLLAPSE_NORTH, Self.ModelData.Data[x, (y + 1)], ((Self.ModelData.Data[x, y].Health * 100) - Intensity), InvisibleARGB)
Else
Self.ModelData.Data[x, y].Collapse(TDDMQuad.COLLAPSE_NORTH, Self.ModelData.Data[x, (y + 1)], TDDMQuad.QUAD_MAXHEALTH, InvisibleARGB)
EndIf
Exit
EndIf
EndIf
Next
Next
Case DIRECTION_EAST
If (StartQuad >= 1)
For x = (Self.ModelData.Width - 1) To 1 Step -1
If (Self.ModelData.Data[x, (StartQuad - 1)].Health > 0)
Self.ModelData.Data[x, (StartQuad - 1)].Deform(TDDMQuad.DEFORM_RIGHT, TDDMQuad.DEFORM_EAST, Self.ModelData.Data[(x - 1), (StartQuad - 1)], InvisibleARGB)
Exit
EndIf
Next
EndIf

If ((StartQuad + Length) < (Self.ModelData.Height - 1))
For x = (Self.ModelData.Width - 1) To 1 Step -1
If (Self.ModelData.Data[x, (StartQuad + Length + 1)].Health > 0)
Self.ModelData.Data[x, (StartQuad + Length + 1)].Deform(TDDMQuad.DEFORM_LEFT, TDDMQuad.DEFORM_EAST, Self.ModelData.Data[(x - 1), (StartQuad + Length + 1)], $00FFFFFF)
Exit
EndIf
Next
EndIf

For y = StartQuad To (StartQuad + Length)
For x = (Self.ModelData.Width - 1) To 1 Step -1
If (Self.ModelData.Data[x, y].Health > 0)
If (Intensity > (Self.ModelData.Data[x, y].Health * 100) And Recursive = True)
Intensity = Intensity - (Self.ModelData.Data[x, y].Health * 100)
Self.SimImpact(DIRECTION_EAST, y, 1, Intensity, True, InvisibleARGB)
Else
If (Recursive = True)
Self.ModelData.Data[x, y].Collapse(TDDMQuad.COLLAPSE_EAST, Self.ModelData.Data[(x - 1), y], ((Self.ModelData.Data[x, y].Health * 100) - Intensity), InvisibleARGB)
Else
Self.ModelData.Data[x, y].Collapse(TDDMQuad.COLLAPSE_EAST, Self.ModelData.Data[(x - 1), y], TDDMQuad.QUAD_MAXHEALTH, InvisibleARGB)
EndIf
Exit
EndIf
EndIf
Next
Next
Case DIRECTION_SOUTH
If (StartQuad >= 1)
For y = (Self.ModelData.Height - 1) To 1 Step -1
If (Self.ModelData.Data[(StartQuad - 1), y].Health > 0)
Self.ModelData.Data[(StartQuad - 1), y].Deform(TDDMQuad.DEFORM_RIGHT, TDDMQuad.DEFORM_SOUTH, Self.ModelData.Data[(StartQuad - 1), (y - 1)], InvisibleARGB)
Exit
EndIf
Next
EndIf

If ((StartQuad + Length) < (Self.ModelData.Width - 1))
For y = (Self.ModelData.Height - 1) To 1 Step -1
If (Self.ModelData.Data[(StartQuad + Length + 1), y].Health > 0)
Self.ModelData.Data[(StartQuad + Length + 1), y].Deform(TDDMQuad.DEFORM_LEFT, TDDMQuad.DEFORM_SOUTH, Self.ModelData.Data[(StartQuad + Length + 1), (y - 1)], InvisibleARGB)
Exit
EndIf
Next
EndIf

For x = StartQuad To (StartQuad + Length)
For y = (Self.ModelData.Height - 1) To 1 Step -1
If (Self.ModelData.Data[x, y].Health > 0)
If (Intensity > (Self.ModelData.Data[x, y].Health * 100) And Recursive = True)
Intensity = Intensity - (Self.ModelData.Data[x, y].Health * 100)
Self.SimImpact(DIRECTION_SOUTH, x, 1, Intensity, True, InvisibleARGB)
Else
If (Recursive = True)
Self.ModelData.Data[x, y].Collapse(TDDMQuad.COLLAPSE_SOUTH, Self.ModelData.Data[x, (y - 1)], ((Self.ModelData.Data[x, y].Health * 100) - Intensity), InvisibleARGB)
Else
Self.ModelData.Data[x, y].Collapse(TDDMQuad.COLLAPSE_SOUTH, Self.ModelData.Data[x, (y - 1)], TDDMQuad.QUAD_MAXHEALTH, InvisibleARGB)
EndIf
Exit
EndIf
EndIf
Next
Next
Case DIRECTION_WEST
If (StartQuad >= 1)
For x = 0 To (Self.ModelData.Width - 2)
If (Self.ModelData.Data[x, (StartQuad - 1)].Health > 0)
Self.ModelData.Data[x, (StartQuad - 1)].Deform(TDDMQuad.DEFORM_LEFT, TDDMQuad.DEFORM_WEST, Self.ModelData.Data[(x + 1), (StartQuad - 1)], InvisibleARGB)
Exit
EndIf
Next
EndIf

If ((StartQuad + Length) < (Self.ModelData.Height - 1))
For x = 0 To (Self.ModelData.Width - 2)
If (Self.ModelData.Data[x, (StartQuad + Length + 1)].Health > 0)
Self.ModelData.Data[x, (StartQuad + Length + 1)].Deform(TDDMQuad.DEFORM_RIGHT, TDDMQuad.DEFORM_WEST, Self.ModelData.Data[(x + 1), (StartQuad + Length + 1)], InvisibleARGB)
Exit
EndIf
Next
EndIf

For y = StartQuad To (StartQuad + Length)
For x = 0 To (Self.ModelData.Width - 2)
If (Self.ModelData.Data[x, y].Health > 0)
If (Intensity > (Self.ModelData.Data[x, y].Health * 100) And Recursive = True)
Intensity = Intensity - (Self.ModelData.Data[x, y].Health * 100)
Self.SimImpact(DIRECTION_WEST, y, 1, Intensity, True, InvisibleARGB)
Else
If (Recursive = True)
Self.ModelData.Data[x, y].Collapse(TDDMQuad.COLLAPSE_WEST, Self.ModelData.Data[(x + 1), y], ((Self.ModelData.Data[x, y].Health * 100) - Intensity), InvisibleARGB)
Else
Self.ModelData.Data[x, y].Collapse(TDDMQuad.COLLAPSE_WEST, Self.ModelData.Data[(x + 1), y], TDDMQuad.QUAD_MAXHEALTH, InvisibleARGB)
EndIf
Exit
EndIf
EndIf
Next
Next
End Select
End Method

Method GetEngineHealth:Double()
If (Self.ModelData.Data <> Null)
Local EngineParts:Double = 0
Local DeadEngine:Double = 0

For Local y:Int = 0 To (Self.ModelData.Height - 1)
For Local x:Int = 0 To (Self.ModelData.Width - 1)
If (Self.ModelData.Data[x, y] <> Null)
If (BitIsSet(Self.ModelData.Data[x, y].FLAGS, TDDMQuad.FLAG_ISENGINE))
EngineParts = EngineParts + 1

If (Self.ModelData.Data[x, y].Health <= 0)
DeadEngine = DeadEngine + 1
EndIf
EndIf
EndIf
Next
Next

If (DeadEngine <> 0 And EngineParts <> 0)
Return (1 - (DeadEngine / EngineParts))
Else
Return 1
EndIf
EndIf
End Method

Method GetTotalHealth:Double()
If (Self.ModelData.Data <> Null)
Local TotalQuads:Double = Self.ModelData.Width * Self.ModelData.Height

Local DeadQuads:Double = 0

For Local y:Int = 0 To (Self.ModelData.Height - 1)
For Local x:Int = 0 To (Self.ModelData.Width - 1)
If (Self.ModelData.Data[x, y] <> Null)
If (Self.ModelData.Data[x, y].Health <= 0)
DeadQuads = DeadQuads + 1
EndIf
EndIf
Next
Next

If (DeadQuads <> 0 And TotalQuads <> 0)
Return (1.0 - (DeadQuads / TotalQuads))
Else
Return 1
EndIf
EndIf
End Method
End Type


und noch ein Beispiel:
BlitzMax: [AUSKLAPPEN]
Graphics(1600, 900)

SetMaskColor(255, 0, 255)

Local VehicleImg:TImage = LoadImage("test.png")
Local VehiclePix:TPixmap = LockImage(VehicleImg)

Local QuadSize:Int = 5
Local TestModel:TDDModel = DDModelFromImage(VehiclePix, QuadSize, 20)
Local TestModel2:TDDModel = DDModelFromImage(VehiclePix, QuadSize)

If (TestModel = Null)
DebugLog("NEIN")
End
EndIf

Local momrot:Float = 0
Local FPS:TTimer = CreateTimer(60)
Local FPSCounter:TFPS = TFPS.Create()

TestModel.SetPosition(400, 400)
TestModel.SetRot(momrot)

For Local y:Int = 1 To 7
For Local x:Int = 4 To 8
TestModel.ModelData.Data[x, y].AddFlag(TDDMQuad.FLAG_ISENGINE)
Next
Next

TestModel2.SetPosition(600, 400)

SetClsColor(255, 255, 255)

Local t:Int

Repeat
WaitTimer(FPS)
Cls

FPSCounter.Update()

If (KeyDown(KEY_LEFT))
momrot = momrot - 1
EndIf

If (KeyDown(KEY_RIGHT))
momrot = momrot + 1
EndIf

If (KeyHit(KEY_R))
TestModel = DDModelFromImage(VehiclePix, QuadSize, 20)
TestModel.SetPosition(400, 400)
TestModel.SetRot(momrot)

For Local y:Int = 1 To 7
For Local x:Int = 4 To 8
If (TestModel.ModelData.Data[x, y] <> Null)
TestModel.ModelData.Data[x, y].AddFlag(TDDMQuad.FLAG_ISENGINE)
EndIf
Next
Next

TestModel2 = DDModelFromImage(VehiclePix, QuadSize)
TestModel2.SetPosition(600, 400)
TestModel2.SetRot(momrot)

For Local y:Int = 1 To 7
For Local x:Int = 4 To 8
If (TestModel2.ModelData.Data[x, y] <> Null)
TestModel2.ModelData.Data[x, y].AddFlag(TDDMQuad.FLAG_ISENGINE)
EndIf
Next
Next
EndIf

If (KeyHit(KEY_W)) ' Norden
TestModel.SimImpact(TDDModel.DIRECTION_NORTH, t = Rand(0, (TestModel.ModelData.Width - 1)), Rand(1, ((TestModel.ModelData.Width - 1) - t)), Rand(75, 200), True, $00FFFFFF)
TestModel2.SimImpact(TDDModel.DIRECTION_NORTH, t = Rand(0, (TestModel.ModelData.Width - 1)), Rand(1, ((TestModel.ModelData.Width - 1) - t)), Rand(75, 200), True, $00FFFFFF)
EndIf

If (KeyHit(KEY_S)) ' Süden
TestModel.SimImpact(TDDModel.DIRECTION_SOUTH, t = Rand(0, (TestModel.ModelData.Width - 1)), Rand(1, ((TestModel.ModelData.Width - 1) - t)), Rand(75, 200), True, $00FFFFFF)
TestModel2.SimImpact(TDDModel.DIRECTION_SOUTH, t = Rand(0, (TestModel.ModelData.Width - 1)), Rand(1, ((TestModel.ModelData.Width - 1) - t)), Rand(75, 200), True, $00FFFFFF)
EndIf

If (KeyHit(KEY_A)) ' Westen
TestModel.SimImpact(TDDModel.DIRECTION_WEST, t = Rand(0, (TestModel.ModelData.Height - 1)), Rand(1, ((TestModel.ModelData.Height - 1) - t)), Rand(75, 200), True, $00FFFFFF)
TestModel2.SimImpact(TDDModel.DIRECTION_WEST, t = Rand(0, (TestModel.ModelData.Height - 1)), Rand(1, ((TestModel.ModelData.Height - 1) - t)), Rand(75, 200), True, $00FFFFFF)
EndIf

If (KeyHit(KEY_D)) ' Osten
TestModel.SimImpact(TDDModel.DIRECTION_EAST, t = Rand(0, (TestModel.ModelData.Height - 1)), Rand(1, ((TestModel.ModelData.Height - 1) - t)), Rand(75, 200), True, $00FFFFFF)
TestModel2.SimImpact(TDDModel.DIRECTION_EAST, t = Rand(0, (TestModel.ModelData.Height - 1)), Rand(1, ((TestModel.ModelData.Height - 1) - t)), Rand(75, 200), True, $00FFFFFF)
EndIf

TestModel.setrot(momrot)
TestModel.Draw()

TestModel2.Draw()
TestModel2.setrot(momrot)

SetColor(0, 0, 0)
DrawText("FPS: " + FPSCounter.Get(), 0, 0)
DrawText("Engine-Health (1): " + (TestModel.GetEngineHealth() * 100) + "%", 0, 40)
DrawText("Total-Health (1): " + (TestModel.GetTotalHealth() * 100) + "%", 0, 60)
DrawText("Engine-Health (2): " + (TestModel2.GetEngineHealth() * 100) + "%", 0, 100)
DrawText("Total-Health (2): " + (TestModel2.GetTotalHealth() * 100) + "%", 0, 120)

Flip 0
Until KeyHit(KEY_ESCAPE)

UnlockImage(VehicleImg)

End


Und das zum Beispiel gehörende Bild:
user posted image

Und die zum Beispiel gehörende Steuerung:
Pfeiltasten links / rechts: Rotieren
Taste W: Zufallsschaden von Norden (relativ)
Taste S: Zufallsschaden von Süden (relativ)
Taste A: Zufallsschaden von Westen (relativ)
Taste D: Zufallsschaden von Osten (relativ)
Taste R: Reparieren

Und eine Funktionsreferenz:
Code: [AUSKLAPPEN]
-> Types:
   -> Type TDDMQuad:
      -> Variablen:
         Field ImageData:TImage
         Field ColorDataRed:Int
         Field ColorDataGreen:Int
         Field ColorDataBlue:Int   
         Field Health:Double   
         Field FLAGS:Byte
         Field QuadSize:Byte   
      -> Konstruktion:
         TDDMQuad.CreateImgQuad:TDDMQuad(ImageSrc:TImage, Health:Double = 100, Flags:Byte = FLAG_NONE, QuadSize:Int = 10)
            -> Beschr.: Erzeugt ein Quad, welches aus einem Bild besteht.
            -> Parameter:
               ImageSrc:TImage - Das Bild
               Health:Double - Die HP des Quads (0 < Health < QUAD_MAXHEALTH)
               Flags:
                  Const FLAG_NONE:Byte = 0      ' Kein Flag gesetzt
                  Const FLAG_ISENGINE:Byte = 1   ' Es handelt sich bei dem Quad um ein EngineQuad, also um ein Quad, das den Motor repräsentiert (für erweiterte "Simulationen")
                  Const FLAG_ISWHEEL:Byte = 2      ' Es handelt sich bei dem Quad um ein WheelQuad, also um ein Quad, das einen Reifen repräsentiert (für erweiterte "Simulationen")
                  Const FLAG_GODQUAD:Byte = 4      ' Es handelt sich um ein unzerstörbares Quad
                  Const FLAG_ISBORDER:Byte = 8   ' Systemflag, nicht benutzen.
                  Const FLAG_ISEDGE:Byte = 16      ' Systemflag, nicht benutzen.
                  Const FLAG_ISCOLORED:Byte = 32   ' Bei dem Quad handelt es sich nicht um ein Image-Quad
                  Const FLAG_DEFORMEDL:Byte = 64   ' Systemflag, nicht benutzen.
                  Const FLAG_DEFORMEDR:Byte = 128   ' Systemflag, nicht benutzen.
               QuadSize:Int - Größe eines Quads (Das Bild muss in den Dimensionen der QuadSize entsprechen)
         TDDMQuad.CreateColQuad:TDDMQuad(ColorRed:Int, ColorGreen:Int, ColorBlue:Int, Health:Double = 100, Flags:Byte = FLAG_ISCOLORED, QuadSize:Int = 10)
            -> Beschr.: Erzeugt ein einfach eingefärbtes Quad
            -> Parameter:
               ColorRed, ColorGreen, ColorBlue - Die Farbanteile des Quads in RGB
               Health - Siehe oben
               Flags - Siehe oben
               QuadSize - Siehe oben
      -> Methoden:
         Method SetHealth(Health:Double)
            -> Beschr.: Setzt die HP des Quads
         Method GetHealth:Double()
            -> Beschr.: Returned die HP des Quads
         Method DrawQuad(PositionX:Double, PositionY:Double)
            -> Beschr.: Malt das Quad (bzw. malt es nicht, falls HP <= 0) an die angegebene BILDSCHIRM-Position
         Method Dent(Direction:Int, InvisibleARGB:Int = $FF000000)
            -> Beschr.: Führt den "Welleneffekt" aus, um so ein beschädigtes Quad zu simulieren
            -> Parameter:
               Direction - Aus welcher Richtung soll der Welleneffekt kommen (An welcher Seite des Modells soll er auftreten?):
                  Const COLLAPSE_NORTH:Int = 0
                  Const COLLAPSE_EAST:Int = 1
                  Const COLLAPSE_SOUTH:Int = 2
                  Const COLLAPSE_WEST:Int = 3
               InvisibleARGB: Die Unsichtbar-Farbe.
         Method Deform(Align:Int, Direction:Int, NextQuad:TDDMQuad, InvisibleARGB:Int = $FF000000)
            -> Beschr.: Erzeugt eine Schräge, um so den Übergang zwischen heilem und kaputtem Quad zu simulieren
            -> Parameter:
               Align - Schräge, die links oder rechts abfällt?
                  Const DEFORM_RIGHT:Int = 0
                  Const DEFORM_LEFT:Int = 1
               Direction - An welcher Seite des Modells soll die Schräge auftreten?:
                  Const DEFORM_NORTH:Int = 0
                  Const DEFORM_EAST:Int = 1
                  Const DEFORM_SOUTH:Int = 2
                  Const DEFORM_WEST:Int = 3
               NextQuad - Das nächte betroffene Quad (Meistens das hinter diesem liegende)
               InvisibleARGB: Die Unsichtbar-Farbe.
         Method Collapse(Direction:Int, NextQuad:TDDMQuad, NewHealth:Double = QUAD_MAXHEALTH, InvisibleARGB:Int = $FF000000)
            -> Beschr.: Handelt es sich bei diesem Quad um ein Rahmen-Quad, so wird es "nach hinten geschoben", das dahinterliegende wird gelöscht
            -> Parameter:
               Direction - An welcher Seite des Modells soll das Quad verschoben werden?:
                  Const COLLAPSE_NORTH:Int = 0
                  Const COLLAPSE_EAST:Int = 1
                  Const COLLAPSE_SOUTH:Int = 2
                  Const COLLAPSE_WEST:Int = 3
               NextQuad - Das nächte betroffene Quad (Meistens das hinter diesem liegende)
               NewHealth - Welche HP soll das neu positionierte Quad nach der Positionsänderung bekommen?
               InvisibleARGB: Die Unsichtbar-Farbe.
         Method AddFlag(Flag:Byte)
            -> Beschr.: Fügt diesem Quad ein neues FLAG hinzu
   -> Type TDDModelQuadData   
      -> Variablen:
         Field Data:TDDMQuad[,]
         Field Width:Int
         Field Height:Int
      -> Konstruktion:
         TDDModelQuadData.Create:TDDModelQuadData(Data:TDDMQuad[,], Width:Int, Height:Int)
            Beschr.: Erzeugt eine neue TDDModelQuadData
   -> Type TDDModel
      -> Variablen:
         Field ModelData:TDDModelQuadData
         Field PositionX:Int
         Field PositionY:Int
         Field Rotation:Double
      -> Konstruktion:
         TDDModel.Create:TDDModel(ModelData:TDDModelQuadData)
            Beschr.: Erzeugt ein neues TDDModel
      -> Methoden:
         Method SyncBorder()
            Beschr.: Diese Methode setzt FLAG_ISBORDER und FLAG_ISEDGE Flags für die Quads des TDDModels. Wird im Konstruktor aufgerufen.
         Method SetPosition(PositionX:Int, PositionY:Int)
            Beschr.: Setzt die BILDSCHIRM-Position
         Method SetRot(Rotation:Double)
            Beschr.: Setzt die Rotation (0 - 359)
         Method Draw()
            Beschr.: Malt das TDDModel an seiner Position und mit seiner Rotation
         Method SimImpact(Direction:Byte, StartQuad:Int, Length:Int, Intensity:Double, Recursive:Byte = True, InvisibleARGB:Int = $FF000000)
            Beschr.: Simuliert einen Aufprall und erzeugt das entsprechende Schadensmodell.
            Parameter:
               Direction - Aus welcher Richtung (relativ) erfolgte der Aufschlag?:
                  Const DIRECTION_NORTH:Byte = 0
                  Const DIRECTION_EAST:Byte = 1
                  Const DIRECTION_SOUTH:Byte = 2
                  Const DIRECTION_WEST:Byte = 3
               StartQuad - Beginn des Schadens (0 bis (Länge der Seite - 1))
               Length - Länge des Schadens (1 bis (Länge der Seite - StartQuad))
               Intesity - Wie stark war der Aufprall (Wie viel HP wird den betroffenen Quads abgezogen)?
               Recursive - Soll der Schaden "weitergereicht" werden? (Ist die Intesity größer als die HP des betroffenen Quads, so wird das dahinterliegende auch noch beschädigt)
               InvisibleARGB: Die Unsichtbar-Farbe.
         Method GetEngineHealth:Double()
            Beschr.: Ermittelt die HP aller Quads, die das Flag FLAG_ISENGINE besitzen und gibt den Prozentwert zurück
         Method GetTotalHealth:Double()
            Beschr.: Ermittelt die HP aller Quads und gibt den Prozentwert zurück
-> Funktionen:
   -> Function DDModelFromImage:TDDModel(ImageSrc:Object, QuadSize:Int = 10, QuadHP:Double = 100)
      Beschr.: Lädt ein DDModel von einem Bild. Dieses Bild muss in den Dimensionen ein Vielfaches der QuadSize sein.
      Parameter:
         ImageSrc:Object - Die Quelle des Bildes
         QuadSize:Int = 10 - Größe eines Quads
         QuadHP:Double = 100 - Welche Standard-HP sollen die Quads bekommen?
      Hinweise:
         Sollte kein Modell geladen werden können, so wird NULL zurückgegeben


Wer dies in einem seiner Projekte verwenden will, den bitte ich, zumindest meinen Namen (Marius Otto) in die Credits zu packen.
Für Lob und Kritik wäre ich euch dankbar.

EDIT:
Hier noch ein zweites Schaubild, diesmal mit einer Quadgröße von 10 x 10 Pixeln:
user posted image

Neue Antwort erstellen


Übersicht Sonstiges Projekte

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group