Seite 1 von 1

Einfaches Rag-Doll Verfahren

Verfasst: 22.01.2014 15:14
von Feindflug
Hi Leute,
ich habe eine kleine Ragdollsimulation mittel PB geschrieben. Dabei habe ich versucht, die einzelnen Bestandteile mit den neuen Modulen zu kapseln. Zur Berechnung der Position mittels Beschleunigungsvariablen habe ich die Verlet Integration Variante gewählt, da sie stabiler ist, als Euler.

Grundsätzlicher Aufbau:
1. Primitives. Das sind Objekte, wie zum Beispiel Kugeln, oder Punkte.
2. Links: Das sind Verbindungen zwischen Primitiven. Diese haben eine Länge und eine Steifheit.
3. Entities: Entitites sind beliebige Kompositionen aus 1 und 2.
4. Hilfskram

Screenshot:
Bild

Code:

Code: Alles auswählen

EnableExplicit


;//////////////////////////////////////////////////////////
;// Math
;//////////////////////////////////////////////////////////

DeclareModule Math
    
    Declare.f fmax(val1.f, val2.f)
    Declare.f fmin(val1.f, val2.f)

EndDeclareModule

Module Math
    Procedure.f fmax(val1.f, val2.f)
        If (val1 < val2)
            ProcedureReturn val2
        Else
            ProcedureReturn val1
        EndIf
    EndProcedure
    
    Procedure.f fmin(val1.f, val2.f)
        If (val1 < val2)
            ProcedureReturn val1
        Else
            ProcedureReturn val2
        EndIf
    EndProcedure
    
EndModule

DeclareModule Vector2
    Structure Type
        x.f
        y.f
    EndStructure
    
    Structure TypeInt
        x.i
        y.i
    EndStructure
    
    Declare.f dot(*vector.Type)
    Declare.f length(*vector.Type)
    Declare   normalize(*vector.Type)
    
    Macro ADD(__RESULT, __VEC1, __VEC2)
        __RESULT\x = __VEC1\x + __VEC2\x
        __RESULT\y = __VEC1\y + __VEC2\y
    EndMacro
    
    Macro ADDSCALAR(__RESULT, __VEC1, __SCALAR)
        __RESULT\x = __VEC1\x + __SCALAR
        __RESULT\y = __VEC1\y + __SCALAR
    EndMacro
    
    
    Macro SUB(__RESULT, __VEC1, __VEC2)
        __RESULT\x = __VEC1\x - __VEC2\x
        __RESULT\y = __VEC1\y - __VEC2\y
    EndMacro
    
    Macro SUBSCALAR(__RESULT, __VEC1, __SCALAR)
        __RESULT\x = __VEC1\x - __SCALAR
        __RESULT\y = __VEC1\y - __SCALAR
    EndMacro
    
    Macro MULSCALAR(__RESULT, __VEC1, __SCALAR)
        __RESULT\x = __VEC1\x * __SCALAR
        __RESULT\y = __VEC1\y * __SCALAR
    EndMacro
    
    Macro DIVSCALAR(__RESULT, __VEC1, __SCALAR)
        If (__SCALAR <> 0.0)
            __RESULT\x = __VEC1\x / __SCALAR
            __RESULT\y = __VEC1\y / __SCALAR
        EndIf
    EndMacro
    
    Macro ASSIGN(__DEST, __SOURCE)
        __DEST\x = __SOURCE\x
        __DEST\y = __SOURCE\y
    EndMacro
    
    Macro CREATE(__NAME, __X, __Y)
        Define.Vector2::Type __NAME
        __NAME\x = __X
        __NAME\y = __Y
    EndMacro
    
    Macro CREATEINT(__NAME, __X, __Y)
        Define.Vector2::TypeInt __NAME
        __NAME\x = __X
        __NAME\y = __Y
    EndMacro
    
    Macro CREATECOPY(__NAME, __OTHER)
        Define.Vector2::Type __NAME
        __NAME\x = __OTHER\x
        __NAME\y = __OTHER\y
    EndMacro
    
EndDeclareModule

Module Vector2
    
    
    Procedure.f dot(*vector.Type)
        ProcedureReturn *vector\x * *vector\x + *vector\y * *vector\y
    EndProcedure
    
    Procedure.f length(*vector.Type)
        ProcedureReturn Sqr(dot(*vector))
    EndProcedure
    
    Procedure normalize(*vector.Type)
        Define.f vecLength = length(*vector)
        DIVSCALAR(*vector, *vector, vecLength)
    EndProcedure

EndModule

;//////////////////////////////////////////////////////////
;// Screen
;//////////////////////////////////////////////////////////

DeclareModule Screen
    
    Macro TRANSFORM2SCREEN(__RESULT, __SOURCE)
        __RESULT\x = Int(__SOURCE\x * ScreenWidth())
        __RESULT\y = Int(__SOURCE\y * ScreenHeight())
    EndMacro
    
    Macro TRANSFORM2WORLD(__RESULT, __SOURCE)
        __RESULT\x = __SOURCE\x / ScreenWidth()
        __RESULT\y = __SOURCE\y / ScreenHeight()
    EndMacro
    
    
EndDeclareModule


Module Screen
EndModule

;//////////////////////////////////////////////////////////
;// Primitives
;//////////////////////////////////////////////////////////

DeclareModule Primitive
    
    Prototype protDrawPrimitive(*primitive)
    Prototype protConstraint(*primitive)
    Prototype protIntegrate(*primitive, elapsedTime.d, dT.d)
    
    Structure Type
        oldPosition.Vector2::Type
        position.Vector2::Type
        fixed.b
        
        draw.protDrawPrimitive
        constraint.protConstraint
        integrate.protIntegrate
    EndStructure
    
    Macro FILL(__PRIMITIVE, __POSITION, __FIXED, __DRAWSTRATEGY, __CONSTRAINT, __INTEGRATE)
        Vector2::ASSIGN(__PRIMITIVE\oldPosition, __POSITION)
        Vector2::ASSIGN(__PRIMITIVE\position, __POSITION)
        __PRIMITIVE\fixed = __FIXED
        __PRIMITIVE\draw = __DRAWSTRATEGY
        __PRIMITIVE\constraint = __CONSTRAINT
        __PRIMITIVE\integrate = __INTEGRATE
    EndMacro
    
    Declare drawPrimitiveCircle(*primitive.Type)
    Declare boxedConstraint(*primitive.Type)
    Declare gravityIntegration(*primitive.Type, elapsedTime.d, dT.d)
    
EndDeclareModule


Module Primitive
    
    Procedure gravityIntegration(*primitive.Type, elapsedTime.d, dT.d)
        If *primitive\fixed = #True
            *primitive\position = *primitive\oldPosition
            ProcedureReturn
        EndIf
        
        Define.Vector2::Type newPosition
        Vector2::ASSIGN(newPosition, *primitive\position)
        Vector2::MULSCALAR(newPosition, newPosition, 2)
        Vector2::SUB(newPosition, newPosition, *primitive\oldPosition)
        
        Vector2::CREATE(gravity, 0.0, 9.81)
        Define.f dT2 = dT * dT
        Vector2::MULSCALAR(gravity, gravity, dT * dT)
        
        Vector2::ADD(newPosition, newPosition, gravity)
        
        Vector2::ASSIGN(*primitive\oldPosition, *primitive\position)
        Vector2::ASSIGN(*primitive\position, newPosition)
    EndProcedure
    
    
    Procedure boxedConstraint(*primitive.Type)
        Define.Vector2::Type *position = @*primitive\position
        
        *position\x = Math::fmax(0.0, *position\x)
        *position\y = Math::fmax(0.0, *position\y)
         
        *position\x = Math::fmin(1.0, *position\x)
        *position\y = Math::fmin(1.0, *position\y)
        
    EndProcedure
    
    
    Procedure drawPrimitiveCircle(*primitive.Type)
        Define.Vector2::TypeInt screenVec
        Screen::TRANSFORM2SCREEN(screenVec, *primitive\position)
        Define.Vector2::Type testVec
        Vector2::ASSIGN(testVec, *primitive\position)
        Screen::TRANSFORM2SCREEN(testVec, *primitive\position)
        
        Circle(screenVec\x, screenVec\y, 4, RGB(255,255,255))    
    EndProcedure
    
EndModule

;//////////////////////////////////////////////////////////
;// Links 
;//////////////////////////////////////////////////////////

DeclareModule Link
    
    Prototype protDrawLink(*Link)
    Prototype protConstraint(*Link)
    
    Structure Type
        *from.Primitive::Type
        *to.Primitive::Type
        
        distance.f
        damping.f
        
        draw.protDrawLink
        constraint.protConstraint
    EndStructure
    
    Macro FILL(__LINK, __FROM, __TO, __DISTANCE, __DAMPING, __DRAWSTRATEGY, __CONSTRAINT)
        __LINK\from = __FROM
        __LINK\to = __TO
        __LINK\distance = __DISTANCE
        __LINK\draw = __DRAWSTRATEGY
        __LINK\constraint = __CONSTRAINT
        __LINK\damping = __DAMPING
    EndMacro
    
    Declare drawLinkLine(*link)
    
    Declare constraintElastic(*link)
    
EndDeclareModule


Module Link
    
    Procedure drawLinkLine(*link.Link::Type)
        Define.Vector2::Type fromPos
        Screen::TRANSFORM2SCREEN(fromPos, *link\from\position)
        Define.Vector2::Type toPos
        Screen::TRANSFORM2SCREEN(toPos, *link\to\position)
        
        LineXY(fromPos\x, fromPos\y, toPos\x, toPos\y, RGB(150,150,150))     
        
    EndProcedure
    
    Procedure constraintElastic(*link.Link::Type)
        Define.Vector2::Type *fromPos = @*link\from\position
        Define.Vector2::Type *toPos = @*link\to\position
        
        ;CallDebugger
        
        Define.Vector2::Type difference
        Vector2::SUB(difference, *toPos, *fromPos)
        
        Define.f dDist = Vector2::length(difference) - *link\distance
        dDist * *link\damping
        dDist * 0.5
        
        Vector2::normalize(@difference)
        Vector2::MULSCALAR(difference, difference, dDist)
        
        If (*link\to\fixed = #False)
            Vector2::SUB(*toPos, *toPos, difference)
        EndIf
        
        If (*link\from\fixed = #False)
            Vector2::ADD(*fromPos, *fromPos, difference)
        EndIf
        
    EndProcedure
    
    
EndModule

;//////////////////////////////////////////////////////////
;// Entity
;//////////////////////////////////////////////////////////

DeclareModule Entity
    
    Prototype protDrawEntity(*entity)
    Prototype protUpdateConstraint(*entity)
    Prototype protIntegrate(*entity, elapsedTime.d, dT.d) 
    
    Structure Container
        Array linkList.Link::Type(1)
        Array primitiveList.Primitive::Type(1)
    EndStructure
    
    Structure Type
        *data.Container
        
        draw.protDrawEntity
        update.protUpdateConstraint
        integrate.protIntegrate
    EndStructure
    
    Declare defaultDraw(*entity.Type)
    Declare defaultConstraint(*entity.Type)
    Declare defaultIntegrate(*entity.Type, elapsedTime.d, dT.d)
    
    Declare createNet(*newEntity.Type, *min.Vector2::Type, *max.Vector2::Type, *dimension.Vector2::TypeInt)
    
EndDeclareModule


Module Entity
    
    Procedure defaultIntegrate(*entity.Type, elapsedTime.d, dT.d)
        Define.Entity::Container *container = *entity\data
        Define.i arrSize = ArraySize(*container\primitiveList())
        For i = 0 To arrSize
            *container\primitiveList(i)\Integrate(*container\primitiveList(i), elapsedTime, dT)
        Next
            
    EndProcedure
    
    
    Procedure defaultConstraint(*entity.Type)
        Define.Entity::Container *container = *entity\data
        Define.i iterations = 2*Int(Sqr(ArraySize(*container\linkList())))+1
        
        For i=0 To iterations
            Define.i arrSize = ArraySize(*container\linkList())
            For i = 0 To arrSize
                *container\linkList(i)\constraint(*container\linkList(i))
            Next
            
            arrSize = ArraySize(*container\primitiveList())
            For i = 0 To arrSize
                *container\primitiveList(i)\constraint(*container\primitiveList(i))
            Next
        Next
        
    EndProcedure
    
    
    Procedure defaultDraw(*entity.Type)
        Define.Entity::Container *container = *entity\data
        Define.i arrSize
        
        ;CallDebugger
        arrSize = ArraySize(*container\linkList())
        For i = 0 To arrSize
            *container\linkList(i)\draw(*container\linkList(i))
        Next
        
        arrSize = ArraySize(*container\primitiveList())
        For i = 0 To arrSize
            *container\primitiveList(i)\draw(*container\primitiveList(i))
        Next
        
    EndProcedure
    
    
    Procedure createNet(*newEntity.Type, *min.Vector2::Type, *max.Vector2::Type, *dimension.Vector2::TypeInt)
        
        *data.Container = AllocateMemory(SizeOf(Container))
        InitializeStructure(*data, Container)
        *newEntity\data = *data
        *newEntity\draw = Entity::@defaultDraw()
        *newEntity\update = Entity::@defaultConstraint()
        *newEntity\integrate = Entity::@defaultIntegrate()
        Define.Vector2::TypeInt reducedDim
        Vector2::SUBSCALAR(reducedDim, *dimension, 1)
        
        Define.i size = *dimension\x * *dimension\y
        Define.i linkSize = *dimension\x * *dimension\y * 2 - *dimension\x - *dimension\y
        ReDim *data\linkList(linkSize - 1)
        ReDim *data\primitiveList(size - 1)
        
        Define.Vector2::Type netSize
        Vector2::SUB(netSize, *max, *min)
        
        Define.f dX = netSize\x / reducedDim\x
        Define.f dY = netSize\y / reducedDim\y
        
        Define.i offset = 0
        For y = 0 To reducedDim\y
            For x = 0 To reducedDim\x
                
                Vector2::CREATE(primPos, x * dX, y * dY)
                Vector2::ADD(primPos, primPos, *min)
                Define fixed = Bool(y=0)
                
                Primitive::FILL(*Data\primitiveList(offset),
                                primPos, 
                                fixed, 
                                Primitive::@drawPrimitiveCircle(),
                                Primitive::@boxedConstraint(),
                                Primitive::@gravityIntegration())
                
                offset + 1
            Next
            
        Next
        
        Define.i counter = 0
        For y = 0 To reducedDim\y
            For x = 0 To reducedDim\x
                
                offset = x + y * *dimension\x
                If (y <> reducedDim\y)
                    Link::FILL(*data\linkList(counter), 
                               *data\primitiveList(offset), 
                               *data\primitiveList(offset+*dimension\y), 
                               dY, 
                               1.0, 
                               Link::@drawLinkLine(), 
                               Link::@constraintElastic())
                    counter+1    
                EndIf
                
                If (x <> reducedDim\x)
                    Link::FILL(*data\linkList(counter), 
                               *data\primitiveList(offset), 
                               *data\primitiveList(offset+1), 
                               dX, 
                               0.3, 
                               Link::@drawLinkLine(), 
                               Link::@constraintElastic())
                    counter+1    
                EndIf
            Next
        Next
    EndProcedure

EndModule


;//////////////////////////////////////////////////////////
;// Main
;//////////////////////////////////////////////////////////

DeclareModule Main
    
    Declare start()
    
EndDeclareModule


Module Main
    
    Global NewList entityList.Entity::Type()
    
    Global.i running = #True
    
    Global elapsedTime.d

    Procedure init()
        InitSprite()
        OpenWindow(0, 0, 0, 600, 400, "Ragdoll", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
        OpenWindowedScreen(WindowID(0), 0, 0, 600, 400)
        
        ;CallDebugger
        
        Vector2::CREATE(netMin, 0.2, 0.2)
        Vector2::CREATE(netMax, 0.8, 0.8)
        Vector2::CREATEINT(dimension, 10, 10)
        AddElement(entityList())
        
        Entity::createNet(entityList(), @netMin, @netMax, @dimension)
        
    EndProcedure
    
    
    Procedure draw()
        
        ClearScreen(RGB(0,0,0))
        StartDrawing(ScreenOutput())
        
        ForEach entityList()
            entityList()\draw(entityList())
        Next
        
        StopDrawing()
        FlipBuffers()
    EndProcedure
    

    Procedure processWindowEvent()
        Define.i event = #PB_Event_None
        Repeat
            event = WindowEvent()

            Select event
                Case #PB_Event_None:
                    ;onIdle()
                    
                Case #PB_Event_CloseWindow:
                    running = false
            EndSelect
        Until event = #PB_Event_None
    EndProcedure
    

    Procedure update()
        Define.d currentTime = ElapsedMilliseconds() * 0.001
        If elapsedTime = 0.0
            elapsedTime = currentTime
        EndIf
        
        Define.d dT = currentTime - elapsedTime
        
        ForEach entityList()
            entityList()\integrate(entityList(), currentTime, dT)
            entityList()\update(entityList())
        Next
        
        elapsedTime = currentTime
    EndProcedure
    
    
    Procedure user()
        ForEach entityList()
            Define.Entity::Container *dataContainer = entityList()\data
            
            Define.i arrSize = ArraySize(*dataContainer\primitiveList())
            For i = 0 To arrSize - 1
                Define.Vector2::Type *position = @*dataContainer\primitiveList(i)\position
                
                Vector2::CREATE(mouse, WindowMouseX(0), WindowMouseY(0))
                Screen::TRANSFORM2WORLD(mouse, mouse)
                
                Vector2::CREATECOPY(difference, mouse)
                Vector2::SUB(difference, difference, *position)
                
                Define.f distance = Vector2::length(difference)
                If (distance < 0.2)
                    distance = 0.2 - distance
                    distance * 0.1
                    Vector2::normalize(difference)
                    Vector2::MULSCALAR(difference, difference, distance)
                    Vector2::SUB(*position, *position, difference)
                EndIf
            Next
            
        Next
            
    EndProcedure
    
    
    Procedure done()
    EndProcedure
    
    
    Procedure start()
        init()
        
        While (running)
            processWindowEvent()
            update()
            draw()
            user()
        Wend
        
        done()
    EndProcedure
    
EndModule

;//////////////////////////////////////////////////////////
;// START
;//////////////////////////////////////////////////////////


Main::start()

Re: Einfaches Rag-Doll Verfahren

Verfasst: 22.01.2014 18:12
von Kukulkan
Toll, macht Spaß :-)

Re: Einfaches Rag-Doll Verfahren

Verfasst: 22.01.2014 18:20
von NicTheQuick
Vielleicht hab ich den Codeteil dafür nicht gefunden, aber wieso zappelt und springt das Teil auch herum, wenn ich gar nichts mache? :|
Und dann noch zwei Kleinigkeiten:

Code: Alles auswählen

    Macro ASSIGN(__DEST, __SOURCE)
        __DEST\x = __SOURCE\x
        __DEST\y = __SOURCE\y
    EndMacro
Das brauchst du eigentlich nicht, da man in PB seit kurzem strukturierte Variablen auch einfach so kopieren kann.

Zweitens: Dein 'Vector2::CREATE' macht zwar ein 'Define', aber nicht das für Procedures eigentlich gedachte 'Protected'. Wenn man also nicht aufpasst, könnte man aus Versehen eine gleichnamige globale Variable in einer Procedure benutzen wollen und bekommt dann den Fehler "[COMPILER] Line 3: Variable already declared with a different scope: a.", wie man an diesem Beispiel sieht:

Code: Alles auswählen

Global a.l = 2
Procedure test()
	Define a.l = 1
	Debug a
EndProcedure

Debug a
test()
Debug a
Aber sonst sieht es schon gut aus. :allright:

Re: Einfaches Rag-Doll Verfahren

Verfasst: 22.01.2014 19:26
von ts-soft
:allright: gefällt mir!

Kleiner Hinweis noch, Dein EnableExplicit wirkt sich nur auf die letzte Zeile aus, das ist in den Modulen nicht gültig!
(dann fällt unter anderem auf, das bei false ein # fehlt :wink: )

Gruß
Thomas

Re: Einfaches Rag-Doll Verfahren

Verfasst: 22.01.2014 20:22
von Feindflug
NicTheQuick hat geschrieben:Vielleicht hab ich den Codeteil dafür nicht gefunden, aber wieso zappelt und springt das Teil auch herum, wenn ich gar nichts mache? :|
Ich nutze den Zeitunterschied zum letzten Frame. Wenn Du jetzt das Fenster verschiebst, springt es exorbitant durch die Gegend.
Wenn nun im Hintergrund Prozesse laufen, die das Programm zu lange ruhen lassen, dann kann es dadurch zum Zittern kommen.
NicTheQuick hat geschrieben:Das brauchst du eigentlich nicht, da man in PB seit kurzem strukturierte Variablen auch einfach so kopieren kann.
Gut zu wissen. Gibts dafür einen Extra-Befehl oder reicht der operator= ?

NicTheQuick hat geschrieben:Zweitens: Dein 'Vector2::CREATE' macht zwar ein 'Define', aber nicht das für Procedures eigentlich gedachte 'Protected'. Wenn man also nicht aufpasst, könnte man aus Versehen eine gleichnamige globale Variable in einer Procedure benutzen wollen und bekommt dann den Fehler "[COMPILER] Line 3: Variable already declared with a different scope: a.", wie man an diesem [...]
Guter Hinweis. Danke.
NicTheQuick hat geschrieben:Aber sonst sieht es schon gut aus. :allright:
Danke schön.

@ts-soft:
Danke für den Hinweis. Ist mir auch schon aufgefallen, dass EnableExplicit nicht keine Auswirkungen auf Module hatte und dachte, es wäre ein Bug ^___^.


Danke für euer Feedback.

Re: Einfaches Rag-Doll Verfahren

Verfasst: 22.01.2014 20:36
von ts-soft
Feindflug hat geschrieben:@ts-soft:
Danke für den Hinweis. Ist mir auch schon aufgefallen, dass EnableExplicit nicht keine Auswirkungen auf Module hatte und dachte, es wäre ein Bug ^___^.
Nein, das ist ein Feature :wink: Das EnableExplicit gehört in jedem Module-Abschnitt, ansonsten würde es ja auch keinen Sinn machen.

Re: Einfaches Rag-Doll Verfahren

Verfasst: 22.01.2014 21:01
von NicTheQuick
Also ausgelastet ist bei mir wirklich wenig, vielleicht ist mein Rechner auch einen Tacken zu schnell und deswegen sind die Millisekunden zwischen einzelnen Frames auch 0, ich weiß es nicht genau. Hab's nicht weiter untersucht. Aber hier ist mal ein Video wie es bei mir aussieht.
Purebasic Rag-Doll-Test

Re: Einfaches Rag-Doll Verfahren

Verfasst: 23.01.2014 17:27
von Feindflug
NicTheQuick: Ich vermute den "Fehler" beim ElapsedMilliseconds()-Befehl. Der scheint mir arg grob zu sein ;-)

Des weiteren müsste ich die Updates des Gitters mit der abgelaufenen Zeitspanne synchronisieren.

Re: Einfaches Rag-Doll Verfahren

Verfasst: 23.01.2014 18:55
von bobobo
nice