Moteur physique 2D ( Verlet )

Programmation d'applications complexes
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Moteur physique 2D ( Verlet )

Message par kelebrindae »

Bonjour,

Voici un petit moteur physique 2D basé sur la méthode de "l'intégration de Verlet".
Je suis tombé par hasard sur l'article suivant: http://www.gamasutra.com/resource_guide ... n_01.shtml et comme ça me paraissait accessible malgré mes faibles connaissances en maths, j'ai tenté le coup.
Le but n'est pas d'avoir une simulation hyper-précise, mais plutôt un petit truc pas trop lourd utilisable dans un jeu de plateformes (ou autre).

Le code est en trois parties:
- un include contenant des macros pour gérer les vecteurs 2;
- un include contenant le moteur physique lui-même (qui fait appel à l'include précédent);
- un petit programme de démo qui utilise l'include ci-dessus.

Contrôles:
- F1 à F9 : créer des objets
- Souris: en laissant le bouton gauche appuyé, on peut déplacer un point à l'écran
- Suppr: supprime tous les objets (ou seulement un seul si le bouton gauche est appuyé)
- Ctrl droit: ralenti (il faut laisser la touche enfoncée)
- Entrée: active/désactive l'affichage (pour les tests de vitesse)

Remarque: à cause de certaines optimisations (pour l'inverse de racine carrée et les pointeurs) je ne suis pas sûr que ça marche en 64 bits, et encore moins sur Mac ou Linux.

Il reste encore deux-trois trucs à terminer, mais comme c'est déjà marrant comme ça, je le poste... :wink:
Allez, je vous laisse tester:
Include 1 (à sauver sous le nom: "vector2.pbi"):

Code : Tout sélectionner

; Author: Kelebrindae
; Date: December, 01, 2010
; PB version: v4.51
; ---------------------------------------------------------------------------------------------------------------
; Description:
; ---------------------------------------------------------------------------------------------------------------
; Vector2 library
; Parts of this code is based on this page: http://www.blitzmax.com/codearcs/codearcs.php?code=2737
; Thanks, Sauer!
; ---------------------------------------------------------------------------------------------------------------

;- Structure and globals
Structure vector2_struct
  x.f
  y.f
EndStructure

Global VEC2_tempLength.f
Global VEC2_tempCondition.b
Global VEC2_tempVectorC.vector2_struct,VEC2_tempVectorD.vector2_struct,VEC2_tempVectorE.vector2_struct
Global VEC2_tempProjVector.vector2_struct,VEC2_tempZeroVector.vector2_struct
Global VEC2_sqrhalf.f,*VEC2_sqrptr,VEC2_sqrInt.i ; used in fast inverse square root macro


;************************************************************************************
; Name: VEC2_INVSQR
; Purpose: Fast inverse square root approximation (faster than 1/sqr(x) )
; Credits: http://www.purebasic.fr/french/viewtopic.php?f=6&t=4113&hilit=sqr
;          http://www.purebasic.fr/french/viewtopic.php?f=6&t=9963
; Parameters:
;   - input  (float)
;   - result (lloat)
;************************************************************************************
Macro VEC2_INVSQR(x,result)
  result = x
  VEC2_sqrhalf = 0.5 * result
  *VEC2_sqrptr = @result
  VEC2_sqrInt  = PeekI(*VEC2_sqrptr)
  
  ; The magic number is different in 64 bits
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    PokeI(*VEC2_sqrptr,$5FE6EB50C7AA19F9 - ( VEC2_sqrInt >> 1))
  CompilerElse ; all the others are 32 bits ?
    PokeI(*VEC2_sqrptr,$5F375A86 - ( VEC2_sqrInt >> 1)) ; or $5f3759df
  CompilerEndIf
  
  result=result*(1.5 - VEC2_sqrhalf * result * result)
  result=result*(1.5 - VEC2_sqrhalf * result * result) ; twice for greater precision
  result=result*(1.5 - VEC2_sqrhalf * result * result) ; thrice is even better (but you can ditch it to squeeze a few extra cycles from your code)
EndMacro


;************************************************************************************
; Name: VEC2_add
; Purpose: Adds two vector
; Parameters:
;   - vector #1 (vector2)
;   - vector #2 (vector2)
;   - result (vector2)
;************************************************************************************
Macro VEC2_add(vectorA,vectorB,vectorResult)
	vectorResult\x = vectorA\x + vectorB\x
	vectorResult\y = vectorA\y + vectorB\y
EndMacro


;************************************************************************************
; Name: VEC2_substract
; Purpose: Substracts two vector
; Parameters:
;   - vector #1 (vector2)
;   - vector #2 (vector2)
;   - result (vector2)
;************************************************************************************
Macro VEC2_substract(vectorA,vectorB,vectorResult)
	vectorResult\x = vectorA\x - vectorB\x
	vectorResult\y = vectorA\y - vectorB\y
EndMacro  


;************************************************************************************
; Name: VEC2_multiply
; Purpose: Multiply a vector by a number
; Parameters:
;   - vector  (vector2)
;   - magnitude (float)
;   - result (vector2)
;************************************************************************************
Macro VEC2_multiply(vectorA,magnitude,vectorResult)
	vectorResult\x = vectorA\x * magnitude
	vectorResult\y = vectorA\y * magnitude
EndMacro  


;************************************************************************************
; Name: VEC2_squareLength
; Purpose: (distance between A and B)²
; Parameters:
;   - coords of point A (vector2)
;   - coords of point B (vector2)
;************************************************************************************
Macro VEC2_squareLength(vectorA,vectorB)
	((vectorA\x - vectorB\x) * (vectorA\x - vectorB\x) + (vectorA\y - vectorB\y) * (vectorA\y - vectorB\y))
EndMacro


;************************************************************************************
; Name: VEC2_length
; Purpose: Distance between A and B
; Parameters:
;   - coords of point A (vector2)
;   - coords of point B (vector2)
;************************************************************************************
Macro VEC2_length(vectorA)
	Sqr( vectorA\x*vectorA\x + vectorA\y*vectorA\y )
EndMacro 


;************************************************************************************
; Name: VEC2_normalize
; Purpose: Normalizes a vector
; Parameters:
;   - vector A (vector2)
;   - result (vector2)
;************************************************************************************
Macro VEC2_normalize(vectorA,vectorResult)
  ; Standard method
  ;VEC2_tempLength = VEC2_length(vectorA)
  ;vectorResult\x = vectorA\x / VEC2_tempLength
  ;vectorResult\y = vectorA\y / VEC2_tempLength
  
  ; Faster
  VEC2_INVSQR(vectorA\x*vectorA\x + vectorA\y*vectorA\y,VEC2_tempLength)
  vectorResult\x = vectorA\x * VEC2_tempLength
  vectorResult\y = vectorA\y * VEC2_tempLength
EndMacro


;************************************************************************************
; Name: VEC2_dotproduct
; Purpose: Dot product of two 2D vector
; Parameters:
;   - vector #1
;   - vector #2
;************************************************************************************
Macro VEC2_dotProduct(vectorA,vectorB)
  (vectorA\x * vectorB\x + vectorA\y * vectorB\y)
EndMacro


;************************************************************************************
; Name: VEC2_crossproduct
; Purpose: Cross product of two 2D vector
; Parameters:
;   - vector #1
;   - vector #2
;************************************************************************************
Macro VEC2_crossProduct(vectorA,vectorB)
  (vectorA\x * vectorB\y - vectorA\y * vectorB\x)
EndMacro


;************************************************************************************
; Name: VEC2_crossVector
; Purpose: Gives the perpendicular vector
; Parameters:
;   - input (vector2)
;   - result (vector2)
;************************************************************************************
Macro VEC2_crossVector(vectorA,vectorResult)
	;returns a vector perpendicular to v
	vectorResult\x = vectorA\y
	vectorResult\y = -vectorA\x
EndMacro


;************************************************************************************
; Name: VEC2_angleBetween
; Purpose: Angle from A to B
; Parameters:
;   - coords of point A (vector2)
;   - coords of point B (vector2)
;************************************************************************************
Macro VEC2_angleBetween(vectorA,vectorB)
	ACos(VEC2_dotProduct(normalize(vectorA),normalize(vectorB)))
EndMacro 


;************************************************************************************
; Name: VEC2_rotate
; Purpose: Rotates a vector
; Parameters:
;   - vector  (vector2)
;   - angle in radians (float)
;   - result (vector2)
;************************************************************************************
Macro VEC2_rotate(vectorA,rad,vectorResult)
	; Counter clockwise rotation, in radians
	vectorResult\x=(Cos(rad)*vectorA\x)-(Sin(rad)*vectorA\y)
	vectorResult\y=(Sin(rad)*vectorA\x)+(Cos(rad)*vectorA\y)
EndMacro 


;************************************************************************************
; Name: VEC2_reflection
; Purpose: Computes a reflection vector, vectorA being the direction and vectorB being 
;          the surface.on which to reflect ("Through" means "through wall" or "bounce 
;          off wall")
; Parameters:
;   - vector #1 (vector2)
;   - vector #2 (vector2)
;   - result (vector2)
;************************************************************************************
Macro VEC2_Reflection(vectorA,vectorB,through,vectorResult)
	If through=#False
	  VEC2_tempVectorC\x = vectorB\x
	  VEC2_tempVectorC\y = vectorB\y
	Else
		VEC2_tempVectorC = VEC2_crossVector(vectorB)
	EndIf 
	VEC2_tempVectorD = VEC2_normalize(vectorA)
	VEC2_tempVectorC = VEC2_normalize(VEC2_tempVectorC)
	VEC2_tempVectorE = VEC2_multiply(VEC2_tempVectorC,VEC2_dotProduct(VEC2_tempVectorD,VEC2_tempVectorC))
	vectorResult = VEC2_substract(VEC2_multiply(VEC2_tempVectorE,2),VEC2_tempVectorD)
	vectorResult = VEC2_normalize(vectorResult)
EndMacro 


;************************************************************************************
; Name: VEC2_collCircleToCircle
; Purpose: Indicates if two circles collide
; Parameters:
;   - center of circle #1 (vector2)
;   - center of circle #2 (vector2)
;   - radius of circle #1 (float)
;   - radius of circle #2 (float)
;   - result (boolean)
;************************************************************************************
Macro VEC2_collCircleToCircle(vectorA,vectorB,radiusA,radiusB,result)
	;simple circle to circle collision using vectors; both circles has the same radius
	If VEC2_squareLength(vectorA,vectorB) < (radius) * (radius)
		result = #True
	Else
		result = #False
	EndIf
EndMacro 


;************************************************************************************
; Name: VEC2_collCircleToVector
; Purpose: Indicates if a circle (with center vectorA and radius) and a vector (with
;          origin vectorB and direction/magnitude.vectorC) are colliding, 
; Parameters:
;   - center of circle (vector2)
;   - radius of circle (float)
;   - vector origin (vector2)
;   - vector direction/magnitude (vector2)
;   - result (boolean)
;************************************************************************************
Macro VEC2_collCircleToVector(vectorA,radius,vectorB,vectorC,result)
  VEC2_tempVectorD = VEC2_substract(vectorA,vectorB)
  VEC2_tempVectorE = normalize(vectorC)
  VEC2_tempProjVector = VEC2_multiply(VEC2_tempVectorE,VEC2_dotProduct(VEC2_tempVectorE,VEC2_tempVectorD))
	VEC2_collCircleToCircle(VEC2_tempVectorD,VEC2_tempProjVector,radius,radius,VEC2_tempCondition)
	VEC2_tempLength = VEC2_length(vectorC) + radius
	VEC2_tempLength = (VEC2_tempLength * VEC2_tempLength)
	If VEC2_tempCondition = #True And VEC2_squareLength(VEC2_tempProjVector,VEC2_tempZeroVector) <= tempVEC2_Length + radius And VEC2_squareLength(VEC2_tempProjVector,vectorC) <= VEC2_tempLength + radius
	  result = #True
	Else
	  result = #False
	EndIf
EndMacro

Dernière modification par kelebrindae le mer. 15/déc./2010 15:38, modifié 1 fois.
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Moteur physique 2D ( Verlet )

Message par kelebrindae »

include 2 (à sauver sous le nom "verlet2D.pbi"):

Code : Tout sélectionner

; Author: Kelebrindae
; Date: December, 15, 2010
; PB version: v4.51
; ---------------------------------------------------------------------------------------------------------------
; Description:
; ---------------------------------------------------------------------------------------------------------------
; A simple 2D physics engine, based on Verlet integration. 
;
; The physics code is based on the following two articles:
;   - http://www.gamasutra.com/resource_guide/20030121/jacobson_01.shtml
;   - http://www.gamedev.net/reference/articles/article2714.asp
; ---------------------------------------------------------------------------------------------------------------
; Known bugs and limitations:
; ---------------------------------------------------------------------------------------------------------------
; - Considering my knowledge of physics and my fluency with vectors, most of the maths in the collision response 
;   are probably wrong. Sorry!
; - TO DO: Procs to weld / separate two points
; - TO DO: Ball simulation (by adding a radius to a single point)
; - TO DO: Adding a "stop" state to bodies under special conditions (near-to-zero speed, resting on an object...)
;          to increase stability.
; ---------------------------------------------------------------------------------------------------------------

;- Constants, structures, globals
; This constant is used to read the lists of points/constraints in memory 
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
  #SIZEOFPTR = 8
CompilerElse
  #SIZEOFPTR = 4
CompilerEndIf


#SUBSAMPLING = 5              ; Greater sub-sampling means slower but more precise simulation (and objects that don't pass through each others)
#CONSTRAINT_ITERATIONS = 4    ; More iterations means that bodies are more rigid
#GRAVITY = 0.005              ; Should be set according to #SUBSAMPLING (gravity is applied in each sub-sampling iteration)
#FRICTION = 0.2               ; Friction for a point colliding against an edge
#FRICTION_EDGEONPOINT = 0.01  ; Friction for a edge colliding against a single point

; 2D vector macro library
IncludeFile "vector2.pbi"

; Pointmass, a.k.a. vertices of a rigid body
Structure pointmass_struct
  x.f     ; Coords x,y
  y.f 
  oldX.f  ; Previous coords x,y
  oldY.f  ; in Verlet integration, velocity, momentum, etc.. are implicit: we just need current and previous coords.
  mass.f    ; mass of the point
  invmass.f ; inverse mass. Set it to zero for static points
EndStructure
Global NewList pointmass.pointmass_struct()

; Constraints, a.k.a. edges of a rigid body
Structure constraint_struct
 *p1.pointmass_struct ; pointer to first point
 *p2.pointmass_struct ; pointer to second point
 enable.b             ; will this constraint be checked for collision ?
 length.f             ; length of the constraint
 length2.f            ; this is equals to length*length (optimization)
 *ptrParent.rigidBody_struct  ; pointer to the parent body
EndStructure
Global NewList constraint.constraint_struct()

; Rigid body
Structure rigidBody_struct
  numint.i  ; Unique ID
  
  nbPoint.i ; Nb points in this body
  nbEdge.i  ; Nb constraints in this body
  *ptrPointList.pointmass_struct        ; pointer to a list of pointers to pointmasses (complicated, but faster than a list() )
  *ptrConstraintList.constraint_struct  ; pointer to a list of pointers to constraints (complicated, but faster than a list() )
  
  ; Point to the parent compound
  *ptrParent.compound_struct
  
  ; These are re-calculated at each frame (for collisions)
  center.vector2_struct
  minX.f
  maxX.f
  minY.f
  maxY.f
EndStructure
Global NewList body.rigidBody_struct()

; Compounds are multi-parts objects, like ropes or bridges. Objects which are part of the same compound don't collide with each other
Structure compound_struct
  name.s
EndStructure
Global NewList compound.compound_struct()

; Used to store collisions datas
Structure collisionInfo_struct
  depth.f
  normal.vector2_struct
  collisionVector.vector2_struct
  
  *ptrEdge.constraint_struct
  *ptrPoint.pointmass_struct
  
  collisionCase.b ; store the collision configuration: no static point, one static point, etc.
EndStructure
Global VERLET_collisionInfo.collisionInfo_struct

; Global vars used in Macros
Global VERLET_i.i,VERLET_j.i,VERLET_numBody.i,VERLET_nbEdges.i
Global VERLET_dx.f,VERLET_dy.f, VERLET_deltalength.f,VERLET_diff.f
Global *VERLET_ptrPoint.pointmass_struct,*VERLET_ptrEdge.constraint_struct,*VERLET_ptrGen,*VERLET_ptrGen2
Global VERLET_axis.vector2_struct,VERLET_tempVector.vector2_struct
Global VERLET_minA.f,VERLET_minB.f,VERLET_maxA.f,VERLET_maxB.f,VERLET_distance.f,VERLET_minDistance.f
Global VERLET_dotP.f
Global VERLET_ratio1.f,VERLET_ratio3.f
Global *VERLET_ptrCollPointBody.rigidBody_struct,*VERLET_ptrCollEdgeBody.rigidBody_struct
Global VERLET_T.f,VERLET_lambda.f,VERLET_edgeMass.f,VERLET_invCollisionMass.f
Global *VERLET_prevBodyPtr.rigidBody_struct, VERLET_startIndex.i, VERLET_isCollision.b


;- Collision and movements

;************************************************************************************
; Name: INTERVAl_DISTANCE
; Purpose: Indicates the minimum distance between the points of two overlapping segments
; Parameters:
;   - segment A min (float)
;   - segment A max (float)
;   - segment B min (float)
;   - segment B max (float)
;   - distance (output float)
;************************************************************************************
Macro INTERVAL_DISTANCE(minA,maxA,minB,maxB,distance)
  If minA < minB
    distance = maxA - minB
  Else
    distance = maxB - minA
  EndIf
EndMacro

;************************************************************************************
; Name: PROJECT_TO_AXIS
; Purpose: Projects the points of a body onto an line
; Parameters:
;   - pointer to a rigid body (*ptr)
;   - axis (vector2)
;   - min (output float)
;   - max (output float)
;************************************************************************************
Macro PROJECT_TO_AXIS (ptrBody,axis,minValue,maxValue)
  *VERLET_ptrGen = ptrBody\ptrPointList
  *VERLET_ptrPoint = PeekI(*VERLET_ptrGen)
  VERLET_dotP = VEC2_dotProduct(axis,*VERLET_ptrPoint)
  minValue = VERLET_dotP
  maxValue = VERLET_dotP
  For VERLET_j = 1 To ptrBody\nbPoint
    *VERLET_ptrGen + #SIZEOFPTR
    *VERLET_ptrPoint = PeekI(*VERLET_ptrGen)
    
    ; Project the rest of the vertices onto the axis and extend the interval to the left/right if necessary
    VERLET_dotP = VEC2_dotProduct(axis,*VERLET_ptrPoint)
    If VERLET_dotP < minValue
      minValue = VERLET_dotP
    EndIf
    If VERLET_dotP > maxValue
      maxValue = VERLET_dotP
    EndIf
  Next VERLET_j
EndMacro    

;************************************************************************************
; Name: COLLISION_RESPONSE
; Purpose: Computes the collision response between a point and a segment. None, one or
;          two points may be static (which leads to different response managements)
; Parameters:
;   None => the needed info is stored in the "VERLET_collisionInfo" structure
;************************************************************************************
Macro COLLISION_RESPONSE()
    ; Collision response = VERLET_collisionInfo\normal * VERLET_collisionInfo\depth
  VEC2_multiply(VERLET_collisionInfo\normal,VERLET_collisionInfo\depth,VERLET_collisionInfo\collisionVector)
  
  ; Edge collision response: we need to know where the point hit the edge
  If( Abs( VERLET_collisionInfo\ptrEdge\p1\x - VERLET_collisionInfo\ptrEdge\p2\x ) > Abs( VERLET_collisionInfo\ptrEdge\p1\y - VERLET_collisionInfo\ptrEdge\p2\y ) )
    VERLET_T = ( VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\collisionVector\x - VERLET_collisionInfo\ptrEdge\p1\x )/(  VERLET_collisionInfo\ptrEdge\p2\x - VERLET_collisionInfo\ptrEdge\p1\x)
  Else
    VERLET_T = ( VERLET_collisionInfo\ptrPoint\y - VERLET_collisionInfo\collisionVector\y - VERLET_collisionInfo\ptrEdge\p1\y )/(  VERLET_collisionInfo\ptrEdge\p2\y - VERLET_collisionInfo\ptrEdge\p1\y)
  EndIf
  VERLET_lambda = 1.0 / ( VERLET_T*VERLET_T + ( 1 - VERLET_T )*( 1 - VERLET_T ) )
  
; Friction of collision with a dynamic body is a little more tricky. You need to calculate relative velocity of colliding points. 
; Let's assume your mass point I collided with and edge (defined by mass points J and K) of the other object. 
;  1 - The relative velocity is relVel = I.pos - I.last_pos - (J.pos + K.pos - J.last_pos - K.last_pos)/2. 
;  2 - you compute tangent from the collision normal and project the relative velocity onto it: relTanVel = tangent*dot(relVel, tangent)
;  3 - you add/subtract (depending on how you computed tangent) some fraction of relTanVel to/from last_pos of I,J,K. 
;      What fraction it will be is determined by I,J,K masses and "hit coefficient" (because I is somewhere between J and K...you get the hit coefficient by projecting I onto the edge JK).

  ; From here, we need the absolute value of the collision normal
  If VERLET_collisionInfo\normal\x < 0
    VERLET_collisionInfo\normal\x = -VERLET_collisionInfo\normal\x
  EndIf
  If VERLET_collisionInfo\normal\y < 0
    VERLET_collisionInfo\normal\y = -VERLET_collisionInfo\normal\y
  EndIf  
  
  ; What's configuration are we in (is there some staic points involved?)
  If VERLET_collisionInfo\ptrPoint\invMass = 0
    VERLET_collisionInfo\collisionCase = %001
  Else
    VERLET_collisionInfo\collisionCase = %000
  EndIf
  If VERLET_collisionInfo\ptrEdge\p1\invmass = 0
    VERLET_collisionInfo\collisionCase + %010
  EndIf
  If VERLET_collisionInfo\ptrEdge\p2\invmass = 0
    VERLET_collisionInfo\collisionCase + %100
  EndIf
  
  ; CASE 1 : no static point
  Select VERLET_collisionInfo\collisionCase
    Case %000 ; no Static point
      ; Mass of the constraint at the intersection point
      VERLET_edgeMass = VERLET_T * VERLET_collisionInfo\ptrEdge\p2\Mass + ( 1 - VERLET_T ) * VERLET_collisionInfo\ptrEdge\p1\Mass
      VERLET_invCollisionMass = 1/( VERLET_edgeMass + VERLET_collisionInfo\ptrPoint\Mass )
      
      ; Response repartition, according to each point's mass
      VERLET_ratio1 = VERLET_collisionInfo\ptrPoint\Mass * VERLET_invCollisionMass
      VERLET_ratio3 = VERLET_edgeMass * VERLET_invCollisionMass;
      
      ; Constraint collision response
      VERLET_collisionInfo\ptrEdge\p1\x + VERLET_collisionInfo\collisionVector\x * ( 1 - VERLET_T ) * VERLET_ratio1 * VERLET_lambda
      VERLET_collisionInfo\ptrEdge\p1\y + VERLET_collisionInfo\collisionVector\y * ( 1 - VERLET_T ) * VERLET_ratio1 * VERLET_lambda
      
      VERLET_collisionInfo\ptrEdge\p2\x + VERLET_collisionInfo\collisionVector\x * VERLET_T  * VERLET_ratio1 * VERLET_lambda
      VERLET_collisionInfo\ptrEdge\p2\y + VERLET_collisionInfo\collisionVector\y * VERLET_T  * VERLET_ratio1 * VERLET_lambda
      
      ; Constraint friction
      VERLET_collisionInfo\ptrEdge\p1\oldX + (VERLET_collisionInfo\ptrEdge\p1\x - VERLET_collisionInfo\ptrEdge\p1\oldX) * VERLET_collisionInfo\normal\y * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p1\oldY + (VERLET_collisionInfo\ptrEdge\p1\y - VERLET_collisionInfo\ptrEdge\p1\oldY) * VERLET_collisionInfo\normal\x * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p2\oldX + (VERLET_collisionInfo\ptrEdge\p2\x - VERLET_collisionInfo\ptrEdge\p2\oldX) * VERLET_collisionInfo\normal\y * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p2\oldY + (VERLET_collisionInfo\ptrEdge\p2\y - VERLET_collisionInfo\ptrEdge\p2\oldY) * VERLET_collisionInfo\normal\x * #FRICTION_EDGEONPOINT
      
      ; Vertex collision response
      VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\collisionVector\x * VERLET_ratio3
      VERLET_collisionInfo\ptrPoint\y - VERLET_collisionInfo\collisionVector\y * VERLET_ratio3
      
      ; Vertex friction
      VERLET_collisionInfo\ptrPoint\oldX + (VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\ptrPoint\oldX) * VERLET_collisionInfo\normal\y * #FRICTION
      
    Case %110 ; point vs. static constraint
      ; Vertex collision response
      VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\collisionVector\x
      VERLET_collisionInfo\ptrPoint\y - VERLET_collisionInfo\collisionVector\y
      
      ; Vertex friction
      VERLET_collisionInfo\ptrPoint\oldX + (VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\ptrPoint\oldX) * VERLET_collisionInfo\normal\y * #FRICTION
      VERLET_collisionInfo\ptrPoint\oldY + (VERLET_collisionInfo\ptrPoint\y - VERLET_collisionInfo\ptrPoint\oldY) * VERLET_collisionInfo\normal\x * #FRICTION
      
    Case %001 ; constraint vs. static point
      VERLET_ratio1 = 1 / (VERLET_T * VERLET_collisionInfo\ptrEdge\p2\Mass + ( 1 - VERLET_T ) * VERLET_collisionInfo\ptrEdge\p1\Mass)
      
      ; Constraint collision response
      VERLET_collisionInfo\ptrEdge\p1\x + VERLET_collisionInfo\collisionVector\x * ( 1 - VERLET_T ) * VERLET_lambda * VERLET_ratio1
      VERLET_collisionInfo\ptrEdge\p1\y + VERLET_collisionInfo\collisionVector\y * ( 1 - VERLET_T ) * VERLET_lambda * VERLET_ratio1
      
      VERLET_collisionInfo\ptrEdge\p2\x + VERLET_collisionInfo\collisionVector\x * VERLET_T * VERLET_lambda * VERLET_ratio1
      VERLET_collisionInfo\ptrEdge\p2\y + VERLET_collisionInfo\collisionVector\y * VERLET_T * VERLET_lambda * VERLET_ratio1
      
      ; Constraint friction
      VERLET_collisionInfo\ptrEdge\p1\oldX + (VERLET_collisionInfo\ptrEdge\p1\x - VERLET_collisionInfo\ptrEdge\p1\oldX) * VERLET_collisionInfo\normal\y * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p1\oldY + (VERLET_collisionInfo\ptrEdge\p1\y - VERLET_collisionInfo\ptrEdge\p1\oldY) * VERLET_collisionInfo\normal\x * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p2\oldX + (VERLET_collisionInfo\ptrEdge\p2\x - VERLET_collisionInfo\ptrEdge\p2\oldX) * VERLET_collisionInfo\normal\y * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p2\oldY + (VERLET_collisionInfo\ptrEdge\p2\y - VERLET_collisionInfo\ptrEdge\p2\oldY) * VERLET_collisionInfo\normal\x * #FRICTION_EDGEONPOINT
      
    Case %010 ; point vs. semi-static constraint (first point is static)
      VERLET_edgeMass = ( 1 - VERLET_T ) * VERLET_collisionInfo\ptrEdge\p2\Mass 
      VERLET_invCollisionMass = 1/( VERLET_edgeMass + VERLET_collisionInfo\ptrPoint\Mass )
      
      VERLET_ratio1 = VERLET_collisionInfo\ptrPoint\Mass * VERLET_invCollisionMass
      VERLET_collisionInfo\ptrEdge\p2\x + VERLET_collisionInfo\collisionVector\x * ( 1 - VERLET_T ) * VERLET_lambda * VERLET_ratio1
      VERLET_collisionInfo\ptrEdge\p2\y + VERLET_collisionInfo\collisionVector\y * ( 1 - VERLET_T ) * VERLET_lambda * VERLET_ratio1
      
      ; Constraint friction
      VERLET_collisionInfo\ptrEdge\p2\oldX + (VERLET_collisionInfo\ptrEdge\p2\x - VERLET_collisionInfo\ptrEdge\p2\oldX) * VERLET_collisionInfo\normal\y * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p2\oldY + (VERLET_collisionInfo\ptrEdge\p2\y - VERLET_collisionInfo\ptrEdge\p2\oldY) * VERLET_collisionInfo\normal\x * #FRICTION_EDGEONPOINT
    
      ; Vertex collision response
      VERLET_ratio3 = VERLET_edgeMass * VERLET_invCollisionMass
      VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\collisionVector\x * VERLET_ratio3
      VERLET_collisionInfo\ptrPoint\y - VERLET_collisionInfo\collisionVector\y * VERLET_ratio3
      
      ; Vertex friction
      VERLET_collisionInfo\ptrPoint\oldX + (VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\ptrPoint\oldX) * VERLET_collisionInfo\normal\y * #FRICTION
      VERLET_collisionInfo\ptrPoint\oldY + (VERLET_collisionInfo\ptrPoint\y - VERLET_collisionInfo\ptrPoint\oldY) * VERLET_collisionInfo\normal\x * #FRICTION
      
    Case %100 ; point vs. semi-static constraint (second point is static)
      ; Constraint collision response
      VERLET_edgeMass = VERLET_T * VERLET_collisionInfo\ptrEdge\p1\Mass
      VERLET_invCollisionMass = 1/( VERLET_edgeMass + VERLET_collisionInfo\ptrPoint\Mass )
      
      VERLET_ratio1 = VERLET_collisionInfo\ptrPoint\Mass * VERLET_invCollisionMass
      VERLET_collisionInfo\ptrEdge\p1\x + VERLET_collisionInfo\collisionVector\x * VERLET_T * VERLET_lambda * VERLET_ratio1
      VERLET_collisionInfo\ptrEdge\p1\y + VERLET_collisionInfo\collisionVector\y * VERLET_T * VERLET_lambda * VERLET_ratio1
      
      ; Constraint friction
      VERLET_collisionInfo\ptrEdge\p1\oldX + (VERLET_collisionInfo\ptrEdge\p1\x - VERLET_collisionInfo\ptrEdge\p1\oldX) * VERLET_collisionInfo\normal\y * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p1\oldY + (VERLET_collisionInfo\ptrEdge\p1\y - VERLET_collisionInfo\ptrEdge\p1\oldY) * VERLET_collisionInfo\normal\x * #FRICTION_EDGEONPOINT
    
      ; Vertex collision response
      VERLET_ratio3 = VERLET_edgeMass * VERLET_invCollisionMass
      VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\collisionVector\x * VERLET_ratio3
      VERLET_collisionInfo\ptrPoint\y - VERLET_collisionInfo\collisionVector\y * VERLET_ratio3
      
      ; Vertex friction
      VERLET_collisionInfo\ptrPoint\oldX + (VERLET_collisionInfo\ptrPoint\x - VERLET_collisionInfo\ptrPoint\oldX) * VERLET_collisionInfo\normal\y * #FRICTION
      VERLET_collisionInfo\ptrPoint\oldY + (VERLET_collisionInfo\ptrPoint\y - VERLET_collisionInfo\ptrPoint\oldY) * VERLET_collisionInfo\normal\x * #FRICTION
      
    Case %011 ; static point vs. semi-static constraint (first point is Static)
      ; Constraint collision response
      VERLET_ratio1 = 1 / (VERLET_T * VERLET_collisionInfo\ptrEdge\p2\Mass )
      VERLET_collisionInfo\ptrEdge\p2\x + VERLET_collisionInfo\collisionVector\x * VERLET_T * VERLET_lambda * VERLET_ratio1
      VERLET_collisionInfo\ptrEdge\p2\y + VERLET_collisionInfo\collisionVector\y * VERLET_T * VERLET_lambda * VERLET_ratio1
      
      ; Constraint friction
      VERLET_collisionInfo\ptrEdge\p2\oldX + (VERLET_collisionInfo\ptrEdge\p2\x - VERLET_collisionInfo\ptrEdge\p2\oldX) * VERLET_collisionInfo\normal\y * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p2\oldY + (VERLET_collisionInfo\ptrEdge\p2\y - VERLET_collisionInfo\ptrEdge\p2\oldY) * VERLET_collisionInfo\normal\x * #FRICTION_EDGEONPOINT
      
    Case %101 ; static point vs. semi-static constraint (second point is Static)
      ; Constraint collision response
      VERLET_ratio1 = 1 / (( 1 - VERLET_T ) * VERLET_collisionInfo\ptrEdge\p1\Mass)
      VERLET_collisionInfo\ptrEdge\p1\x + VERLET_collisionInfo\collisionVector\x * ( 1 - VERLET_T ) * VERLET_lambda * VERLET_ratio1
      VERLET_collisionInfo\ptrEdge\p1\y + VERLET_collisionInfo\collisionVector\y * ( 1 - VERLET_T ) * VERLET_lambda * VERLET_ratio1
      
      ; Constraint friction
      VERLET_collisionInfo\ptrEdge\p1\oldX + (VERLET_collisionInfo\ptrEdge\p1\x - VERLET_collisionInfo\ptrEdge\p1\oldX) * VERLET_collisionInfo\normal\y * #FRICTION_EDGEONPOINT
      VERLET_collisionInfo\ptrEdge\p1\oldY + (VERLET_collisionInfo\ptrEdge\p1\y - VERLET_collisionInfo\ptrEdge\p1\oldY) * VERLET_collisionInfo\normal\x * #FRICTION_EDGEONPOINT
      
  EndSelect
  
EndMacro

;************************************************************************************
; Name: DETECT_COLLISION
; Purpose: Determines if two rigid bodies are colliding, using SAT algorithm
;           http://www.gamedev.net/reference/articles/article2714.asp
; Parameters:
;   - pointer to first body
;   - pointer to second body
;   - collision or not (output boolean)
; Note: This macro looks for a collision between a point and an edge (= a constraint).
;       The structure "collisionInfo" stores the pointer to these, as well as the
;       collsion depth, the collision normal, etc..
;************************************************************************************
Macro DETECT_COLLISION(ptrBody1,ptrBody2,collisionResult)
  
  ; We check all edges of the two bodies
  VERLET_nbEdges = ptrBody1\nbEdge + ptrBody2\nbEdge + 1
  *VERLET_ptrGen2 = ptrBody1\ptrConstraintList
  VERLET_minDistance = 10000000
  collisionResult = #True
  For VERLET_i = 0 To VERLET_nbEdges
    *VERLET_ptrEdge = PeekI(*VERLET_ptrGen2)
    ; When all of body1's edges have been checked, switch to body2
    If VERLET_i = ptrBody1\nbEdge
      *VERLET_ptrGen2 = ptrBody2\ptrConstraintList
    Else
      *VERLET_ptrGen2 + #SIZEOFPTR
    EndIf
    
    If *VERLET_ptrEdge\enable = #False
      Continue
    EndIf
    
    ; Calculate the axis perpendicular (cross vector) to this edge and normalize it
    VERLET_axis\x = *VERLET_ptrEdge\p1\y - *VERLET_ptrEdge\p2\y
    VERLET_axis\y = *VERLET_ptrEdge\p2\x - *VERLET_ptrEdge\p1\x
    VEC2_Normalize(VERLET_axis,VERLET_axis)
    
    ; Project each point of the 2 bodies on this vector; Get min/max for each body
    PROJECT_TO_AXIS(ptrBody1,VERLET_axis,VERLET_minA,VERLET_maxA)
    PROJECT_TO_AXIS(ptrBody2,VERLET_axis,VERLET_minB,VERLET_maxB)
    
    ; Calculate the distance between the two intervals
    INTERVAL_DISTANCE( VERLET_minA, VERLET_maxA, VERLET_minB, VERLET_maxB, VERLET_distance)
       
    ; If the intervals don't overlap, there is no collision
    If VERLET_distance < 0.0       
      collisionResult = #False
      Break
    EndIf     
    
    ; Note: if body contains parallel lines, the "minDistance" edge might not be the good one...
    ; => if distance = minDistance, we should check if the edge is closer to the second body
    If VERLET_distance < VERLET_minDistance
      VERLET_minDistance = VERLET_distance
      VERLET_collisionInfo\normal\x = VERLET_axis\x
      VERLET_collisionInfo\normal\y = VERLET_axis\y
      VERLET_collisionInfo\ptrEdge = *VERLET_ptrEdge
    EndIf
  
  Next VERLET_i
  
  ; If the two bodies collide, what do we do ?
  If collisionResult = #True
    VERLET_collisionInfo\depth = VERLET_minDistance
    
    ; To make the folowing code simplier, we make sure that we have:
    ;   - a body #1 containing the colliding point
    ;   - a body #2 containing the colliding edge
    If VERLET_collisionInfo\ptrEdge\ptrParent = ptrBody2
      *VERLET_ptrCollPointBody = ptrBody1
      *VERLET_ptrCollEdgeBody = ptrBody2
    Else
      *VERLET_ptrCollPointBody = ptrBody2
      *VERLET_ptrCollEdgeBody = ptrBody1
    EndIf
    
    ; The collision normal must point at body #1
    VEC2_substract(*VERLET_ptrCollEdgeBody\center,*VERLET_ptrCollPointBody\center,VERLET_tempVector)
    ; if not, revert the collision normal
    If VEC2_dotproduct(VERLET_collisionInfo\normal,VERLET_tempVector) < 0
      VERLET_collisionInfo\normal\x = -VERLET_collisionInfo\normal\x
      VERLET_collisionInfo\normal\y = -VERLET_collisionInfo\normal\y
    EndIf
  
    ; The colliding vertex is the one closer to the center of body #2
    ; So we measure the distance of the vertex from the line using the line equation
    *VERLET_ptrGen = *VERLET_ptrCollPointBody\ptrPointList
    *VERLET_ptrPoint = PeekI(*VERLET_ptrGen)
    VEC2_substract(*VERLET_ptrCollEdgeBody\center,*VERLET_ptrPoint,VERLET_tempVector)
    VERLET_distance = VEC2_dotProduct(VERLET_collisionInfo\normal,VERLET_tempVector)
    VERLET_minDistance = VERLET_Distance
    VERLET_collisionInfo\ptrPoint = *VERLET_ptrPoint    
    For VERLET_i = 1 To *VERLET_ptrCollPointBody\nbPoint
      *VERLET_ptrGen + #SIZEOFPTR
      *VERLET_ptrPoint = PeekI(*VERLET_ptrGen)
      
      ; Measure the distance of the vertex from the line using the line equation
      VEC2_substract(*VERLET_ptrCollEdgeBody\center,*VERLET_ptrPoint,VERLET_tempVector)
      VERLET_distance = VEC2_dotProduct(VERLET_collisionInfo\normal,VERLET_tempVector)
      
      ; If the measured distance is smaller than the min distance, we've got our collision vertex
      If VERLET_distance < VERLET_minDistance
        VERLET_minDistance = VERLET_Distance
        VERLET_collisionInfo\ptrPoint = *VERLET_ptrPoint
      EndIf
    Next VERLET_i
    
  ;    Protected w.i,h.i
  ; ;  If (*VERLET_ptrCollPointBody\name = "bascule" And Left(*VERLET_ptrCollEdgeBody\name,6) = "Bridge") Or (*VERLET_ptrCollEdgeBody\name = "bascule" And Left(*VERLET_ptrCollPointBody\name,6) = "Bridge")
  ;     Repeat
  ;       ClearScreen($000001)
  ;       StartDrawing(ScreenOutput())
  ;       drawconstraints()
  ;       drawpointmasses()
  ;       Circle(VERLET_collisionInfo\ptrPoint\x,VERLET_collisionInfo\ptrPoint\y,2,$0000FF)
  ;       w = VERLET_collisionInfo\ptrEdge\p2\x - VERLET_collisionInfo\ptrEdge\p1\x
  ;       h = VERLET_collisionInfo\ptrEdge\p2\y - VERLET_collisionInfo\ptrEdge\p1\y
  ;       If w = 0
  ;         w = 1
  ;       EndIf
  ;       If h = 0
  ;         h = 1
  ;       EndIf
  ;       Line(VERLET_collisionInfo\ptrEdge\p1\x,VERLET_collisionInfo\ptrEdge\p1\y,w,h,$0000FF)
  ;       ;Line(*VERLET_ptrCollEdgeBody\center\x,*VERLET_ptrCollEdgeBody\center\y,VERLET_axis\x,VERLET_axis\y,$00FFFF)
  ;       StopDrawing()
  ;       FlipBuffers()
  ;       ExamineKeyboard()      
  ;     Until KeyboardReleased(#PB_Key_Space)
  ; ;   EndIf   
  
    ; Compute the collision response
    COLLISION_RESPONSE()

  EndIf ; if collisionResult = #true...
  
  
EndMacro

;************************************************************************************
; Name: UPDATE_POINTMASSES
; Purpose: Update position of each pointmass and add gravity (except for the static points)
; Parameters:
;   None
;************************************************************************************
Macro UPDATE_POINTMASSES()
  
  ForEach pointmass()
    If pointmass()\invmass <> 0
      
      ;gets the velocity  
		  VERLET_dx = pointmass()\x - pointmass()\oldX	
		  VERLET_dy = pointmass()\y - pointmass()\oldY + #GRAVITY 
		  
	    ;updates oldX and oldY
  		pointmass()\oldX = pointmass()\x
  		pointmass()\oldY = pointmass()\y
  		
  		;adds velocity to get new x and new y
  		pointmass()\x + VERLET_dx 	
  		pointmass()\y + VERLET_dy 
  		
  		If pointmass()\x > #SCREENWIDTH 
  			pointmass()\x = #SCREENWIDTH
  			VERLET_dy = pointmass()\y - pointmass()\oldY
  			pointmass()\oldY + VERLET_dy * #FRICTION	;simulates friction
  		EndIf
  		If pointmass()\x < 0 
  			pointmass()\x = 0
  			VERLET_dy = pointmass()\y - pointmass()\oldY
  			pointmass()\oldY + VERLET_dy * #FRICTION	;simulates friction
  		EndIf

  		If pointmass()\y > #SCREENHEIGHT 
  			pointmass()\y = #SCREENHEIGHT
  			VERLET_dx = pointmass()\x - pointmass()\oldX
  			pointmass()\oldX + VERLET_dx * #FRICTION	;simulates friction
  		EndIf
  		
  	EndIf
  Next pointmass()
  
EndMacro

;************************************************************************************
; Name: UPDATE_CONSTRAINTS
; Purpose: Once the points moved, the constraints do their best to retrieve their
;          initial length
; Parameters:
;   None
;************************************************************************************
Macro UPDATE_CONSTRAINTS()

  For VERLET_i = 1 To #CONSTRAINT_ITERATIONS	 ;this is necessary with many constraints to solve them correctly
    ForEach constraint()
	    
	    If constraint()\p1\invmass > 0 Or constraint()\p2\invmass > 0
  	    VERLET_dx = constraint()\p2\x - constraint()\p1\x
  	    VERLET_dy = constraint()\p2\y - constraint()\p1\y
  	    
  	    ; Correction to apply
   			;VERLET_deltalength = Sqr(VERLET_dx*VERLET_dx + VERLET_dy*VERLET_dy)	;distance formula
   			;VERLET_diff = (VERLET_deltalength - constraint()\length) / (VERLET_deltalength*(constraint()\p1\invmass+constraint()\p2\invmass)) 			
  
        ; Same thing, but without SQR (faster!)
        ;length2 = constraint()\length*constraint()\length ; optimisation: length2 is stored
        VERLET_diff = ( (constraint()\length2 / ((VERLET_dx*VERLET_dx + VERLET_dy*VERLET_dy) + constraint()\length2)) - 0.5) / ((constraint()\p1\invmass + constraint()\p2\invmass) * -0.5)
        
    	  constraint()\p1\x + (VERLET_diff * VERLET_dx * constraint()\p1\invmass)
    	  constraint()\p1\y + (VERLET_diff * VERLET_dy * constraint()\p1\invmass)
    	  constraint()\p2\x - (VERLET_diff * VERLET_dx * constraint()\p2\invmass)
    	  constraint()\p2\y - (VERLET_diff * VERLET_dy * constraint()\p2\invmass)
    	EndIf
    	
    Next constraint()
	Next VERLET_i	
	
EndMacro

;************************************************************************************
; Name: MANAGE_COLLISIONS
; Purpose: Check collision for each body against all the others (include collision
;          response => bounce)
; Parameters:
;   None
;************************************************************************************
Macro MANAGE_COLLISIONS()
  
    ; Compute center and "collision box" for each rigid body
    ForEach body()     
      *ptr = body()\ptrPointList
      *ptrPoint = PeekI(*ptr)
      body()\center\x = *ptrPoint\x
      body()\center\y = *ptrPoint\y
      body()\minX = *ptrPoint\x
      body()\maxX = *ptrPoint\x
      body()\minY = *ptrPoint\y
      body()\maxY = *ptrPoint\y
      
      For VERLET_i = 1 To body()\nbPoint
        *ptr + #SIZEOFPTR
        *ptrPoint = PeekI(*ptr)
        body()\center\x + *ptrPoint\x
        body()\center\y + *ptrPoint\y
        If *ptrPoint\x < body()\minX
          body()\minX = *ptrPoint\x
        EndIf
        If *ptrPoint\x > body()\maxX
          body()\maxX = *ptrPoint\x
        EndIf
        If *ptrPoint\y < body()\minY
          body()\minY = *ptrPoint\y
        EndIf
        If *ptrPoint\y > body()\maxY
          body()\maxY = *ptrPoint\y
        EndIf
      Next VERLET_i

      ; At this point, VERLET_i = body()\nbPoint + 1
      body()\center\x / VERLET_i
      body()\center\y / VERLET_i
    Next body()
    
    ; Collisions: Check each body against all the others
    ForEach body()
      *VERLET_prevBodyPtr = @body()
      VERLET_startIndex = ListIndex(body())
      ForEach body()
        If ListIndex(body()) > VERLET_startIndex And (*VERLET_prevBodyPtr\ptrParent = 0 Or body()\ptrParent = 0 Or *VERLET_prevBodyPtr\ptrParent <> body()\ptrParent)
          If ( *VERLET_prevBodyPtr\MinX <= body()\MaxX ) And ( *VERLET_prevBodyPtr\MinY <= body()\MaxY ) And ( *VERLET_prevBodyPtr\MaxX >= body()\MinX ) And ( *VERLET_prevBodyPtr\MaxY >= body()\MinY )
            DETECT_COLLISION(*VERLET_prevBodyPtr,body(),VERLET_isCollision)
          EndIf
        EndIf
      Next body()
      ChangeCurrentElement(body(),*VERLET_prevBodyPtr)
    Next body()
    
EndMacro    


;- Points / constraints creation

;************************************************************************************
; Name: createPointmass
; Purpose: Creates a pointmass
; Parameters:
;   - coords x,y (float)
;   - mass (optional float)
;   - initial velocities x,y (optional float)
; Returns : pointer to newly created pointmass
;************************************************************************************
Procedure createPointmass(x.f,y.f,mass.f = 1,vx.f = 0 ,vy.f = 0)	; x and y are coords for the verlet. vx and vy are velocity values for the verlet
  
  AddElement(pointmass())
  pointmass()\x = x
  pointmass()\y = y
  pointmass()\oldX = x - vx	;gives the particle a starting velocity
  pointmass()\oldY = y - vy
  If mass > 0
    pointmass()\mass = mass
    pointmass()\invmass = 1 / mass
  Else
    pointmass()\mass = 999999
    pointmass()\invmass = 0
  EndIf
  
  ProcedureReturn @pointmass()
EndProcedure


;************************************************************************************
; Name: createConstraint
; Purpose: Creates a constraint
; Parameters:
;   - pointer to first point (ptr)
;   - pointer to second point (ptr)
;   - is this constraint can be collided (optional boolean)
; Returns : pointer to newly created constraint
;************************************************************************************
Procedure createConstraint(*p1.pointmass_struct,*p2.pointmass_struct,enable.b = #True)
  
  AddElement(constraint())
	constraint()\p1 = *p1
	constraint()\p2 = *p2
	constraint()\enable = enable
	constraint()\length = Sqr( Pow((constraint()\p1\x - constraint()\p2\x),2) + Pow((constraint()\p1\y - constraint()\p2\y),2) )
	constraint()\length2 = constraint()\length * constraint()\length ; square length. Stored for optimization
	
	ProcedureReturn @constraint()

EndProcedure


;************************************************************************************
; Name: createBody
; Purpose: Creates a rigid body
; Parameters:
;   None (points and constraints will be added later)
; Returns : pointer to newly created body
;************************************************************************************
Procedure createBody()
  VERLET_numBody + 1
  
  AddElement(body())
  body()\numint = VERLET_numBody
  body()\nbPoint = -1
  body()\nbEdge = -1
  
  ProcedureReturn @body()  
EndProcedure

;************************************************************************************
; Name: addBodyPointmass
; Purpose: Creates a pointmass and adds it to an already created body
; Parameters:
;   - pointer to the body (ptr)
;   - coords x,y (float)
;   - mass (optional float)
;   - initial velocities x,y (optional float)
; Returns : pointer to newly created pointmass
;************************************************************************************
Procedure addBodyPointmass(*ptrBody.rigidBody_struct ,x.f,y.f,mass.f = 1,vx.f = 0 ,vy.f = 0)
  Protected *ptr.pointmass_struct
  
  ; Make room for the new point in the body's list
  *ptrBody\nbPoint + 1
  If *ptrBody\nbPoint = 0
    *ptrBody\ptrPointList = AllocateMemory((*ptrBody\nbPoint + 1) * #SIZEOFPTR )
  Else
    *ptrBody\ptrPointList = ReAllocateMemory(*ptrBody\ptrPointList,(*ptrBody\nbPoint + 1) * #SIZEOFPTR )
  EndIf
  
  ; Create the point and store its pointer in the list
  *ptr = createPointmass(x,y,mass,vx,vy)
  PokeI(*ptrBody\ptrPointList + *ptrBody\nbPoint * #SIZEOFPTR , *ptr )
  
  ProcedureReturn *ptr
EndProcedure


;************************************************************************************
; Name: addBodyConstraint
; Purpose: Creates a constraint and adds it to an already created body
; Parameters:
;   - pointer to the body (ptr)
;   - pointer to first point (ptr)
;   - pointer to second point (ptr)
;   - is this constraint can be collided (optional boolean)
; Returns : pointer to newly created constraint
;************************************************************************************
Procedure addBodyConstraint(*ptrBody.rigidBody_struct,*p1.pointmass_struct,*p2.pointmass_struct,enable.b = #True)
  Protected *ptr.constraint_struct
  
  ; Make room for the new constraint in the body's list
  *ptrBody\nbEdge + 1
  If *ptrBody\nbEdge = 0
    *ptrBody\ptrConstraintList = AllocateMemory((*ptrBody\nbEdge + 1) * #SIZEOFPTR )
  Else
    *ptrBody\ptrConstraintList = ReAllocateMemory(*ptrBody\ptrConstraintList,(*ptrBody\nbEdge + 1) * #SIZEOFPTR )
  EndIf
      
  ; Create the constraint and store its pointer in the list
  *ptr = createConstraint(*p1,*p2,enable)
  PokeI(*ptrBody\ptrConstraintList + *ptrBody\nbEdge * #SIZEOFPTR, *ptr)
  *ptr\ptrParent = *ptrBody
   
  ProcedureReturn *ptr
EndProcedure

Les idées sont le souvenir de choses qui ne se sont pas encore produites.
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Moteur physique 2D ( Verlet )

Message par kelebrindae »

:twisted: 'Tain! ça fait une heure que j'essaie d'envoyer le dernier bout de code, ça marche pas! :twisted:
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Moteur physique 2D ( Verlet )

Message par djes »

C'est pas bien de nous faire baver comme ça :P
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Moteur physique 2D ( Verlet )

Message par kelebrindae »

:x RRRAAAAH! Rien à faire, le forum ne veut pas accepter le code. :x

Puisque c'est comme ça, je mets un zip avec les trois fichiers dedans à cette adresse:
http://jeeswee.free.fr/files/verlet.zip

(pfff! ça m'épuise, des trucs comme ça...)
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
Avatar de l’utilisateur
Huitbit
Messages : 940
Inscription : jeu. 08/déc./2005 5:19
Localisation : Guadeloupe

Re: Moteur physique 2D ( Verlet )

Message par Huitbit »

Superbe !
Elevé au MSX !
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Moteur physique 2D ( Verlet )

Message par flaith »

hé kelebrindae, devine, le clearscreen ne fait rien chez moi :mrgreen:
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Moteur physique 2D ( Verlet )

Message par djes »

Incontestablement l'un des plus beaux codes du forum. Chapeau!
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Moteur physique 2D ( Verlet )

Message par djes »

Je me permets d'essayer d'envoyer le code...

Code : Tout sélectionner

; Author: Kelebrindae
; Date: December, 15, 2010
; PB version: v4.51
; ---------------------------------------------------------------------------------------------------------------
; Description:
; ---------------------------------------------------------------------------------------------------------------
; Demo for my Verlet 2D physics engine.
;
; F1 to F9 : Create pre-defined sets of objects
; Left mouse button: Pick an object
; Del: Delete the picked object (or all objects if none is picked)
; Return: Enable/disable drawing (for benchmarking)
; Right-Ctrl : Slow motion
; ---------------------------------------------------------------------------------------------------------------
; Known bugs and limitations:
; ---------------------------------------------------------------------------------------------------------------
; - The drawing  method used in this demo is quite slow. To test the real speed of the engine, use the 
;   "#PB_Screen_NoSynchronization" flag in OpenWindowedScreen and press "Return" (disables drawing)
; ---------------------------------------------------------------------------------------------------------------

; Window size
#SCREENWIDTH = 800
#SCREENHEIGHT = 500

; Verlet integration library
IncludeFile "verlet2D.pbi"

; General purpose variables
Global Dim *p.pointmass_struct(20)
Global *body.rigidBody_struct
Global drawmode.b = #True, mousemode.b = #False
Global *mousePoint.pointmass_struct, *ptrPoint.pointmass_struct

;- --- Procedures ---
EnableExplicit

;********************************************************
;- Drawing procedures
;********************************************************

; Draw all the points
Procedure drawpointmasses()
  Protected w.i,h.i
  
  ForEach pointmass()
    ; not static => white circle
    If pointmass()\invmass > 0
      Circle(pointmass()\x,pointmass()\y,2,$FFFFFF)
      
      ; also, we draw the speed as yellow lines (but most of the time, it's invisible)
      w = pointmass()\oldX - pointmass()\x
      h = pointmass()\oldY - pointmass()\y
      If w = 0
        w = 1
      EndIf
      If h = 0
        h = 1
      EndIf
      Line(pointmass()\x,pointmass()\y,w,h,$00FFFF)
    Else ; static => blue cross
      Line(pointmass()\x - 3,pointmass()\y - 3,7,7,$FF0000)
      Line(pointmass()\x - 3,pointmass()\y + 3,7,-7,$FF0000)
    EndIf
  Next pointmass()

EndProcedure

; Draw all the constraints
Procedure drawconstraints()
  Protected w.i,h.i
  
  ForEach constraint()
    w = constraint()\p2\x - constraint()\p1\x
    h = constraint()\p2\y - constraint()\p1\y
    
    If w = 0
      w = 1
    EndIf
    If h = 0
      h = 1
    EndIf
    
    ; Draw constraints in green, or grey is they are disabled
    If constraint()\enable = #True
      Line(constraint()\p1\x,constraint()\p1\y,w,h,$00FF00)
    Else
      Line(constraint()\p1\x,constraint()\p1\y,w,h,$777777)
    EndIf
  Next constraint()

EndProcedure



;********************************************************
;- Pre-defined objects
;********************************************************

; Create a single line. Useful to figure the ground, the walls, etc..
; (It's made of two points and TWO constraints, because my collision algo doesn't seem to like single constraint bodies...)
Procedure createLine(x1.i,y1.i,x2.i,y2.i,mass.f = 1)
  Protected *body.rigidBody_struct
  *body = createBody()
  
  *p(1) = addBodyPointmass(*body,x1,y1,mass)
  *p(2) = addBodyPointmass(*body,x2,y2,mass)
  
  addBodyConstraint(*body,*p(1),*p(2))
  addBodyConstraint(*body,*p(2),*p(1))
  
  ProcedureReturn *body
EndProcedure

; Create a triangle
Procedure createTriangle(x.i,y.i,width.i,height.i,mass.f = 1,hspeed.f = 0,vspeed.f = 0,rotation.f = 0)
  Protected *body.rigidBody_struct
  *body = createBody()
  
  *p(1) = addBodyPointmass(*body,x,y,mass,hspeed+rotation,vspeed)
  *p(2) = addBodyPointmass(*body,x,y+height,mass,hspeed,vspeed)
  *p(3) = addBodyPointmass(*body,x+width,y,mass,hspeed,vspeed+rotation)

  addBodyConstraint(*body,*p(1),*p(2))
  addBodyConstraint(*body,*p(1),*p(3))
  addBodyConstraint(*body,*p(2),*p(3))
  
  ProcedureReturn *body  
EndProcedure


; Create a box
Procedure createBox(x.i,y.i,width.i,height.i,mass.f = 1,hspeed.f = 0,vspeed.f = 0,rotation.f = 0)
  Protected *body.rigidBody_struct
  *body = createBody()
  
  *p(1) = addBodyPointmass(*body,x,y,mass,hspeed+rotation,vspeed)
  *p(2) = addBodyPointmass(*body,x,y+height,mass,hspeed,vspeed)
  *p(3) = addBodyPointmass(*body,x+width,y+height,mass,hspeed-rotation,vspeed)
  *p(4) = addBodyPointmass(*body,x+width,y,mass,hspeed+rotation,vspeed)
  
  addBodyConstraint(*body,*p(1),*p(2))
  addBodyConstraint(*body,*p(2),*p(3))
  addBodyConstraint(*body,*p(3),*p(4))
  addBodyConstraint(*body,*p(1),*p(4))
  addBodyConstraint(*body,*p(1),*p(3),#False)
  addBodyConstraint(*body,*p(2),*p(4),#False)
  
  ProcedureReturn *body  
EndProcedure


; Create a chain of lines which can be used as a rope, an elastic, a bridge, etc..
Procedure createRope(x1.i,y1.i,x2.i,y2.i,nbSegments.i)
  Protected i.i
  Protected x.f = x1,y.f = y1
  Protected xd.f = (x2 - x1) / nbSegments,yd.f = (y2 - y1) / nbSegments
  Protected *body.rigidBody_struct
  
  AddElement(compound())
  
  For i=1 To nbSegments
    *body = createBody()
    If i=1
      *p(1) = addBodyPointmass(*body,x,y,0) ; mass = 0 => static point
    Else
      *p(1) = *p(2)
      ; Re-use previous point
      *body\nbPoint + 1
      *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
      PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(1) )
    EndIf
        
    ; add next point
    x+xd
    y+yd
    *p(2) = addBodyPointmass(*body,x,y)
    
    ; create link between the two points
    addBodyConstraint(*body,*p(1),*p(2))
    addBodyConstraint(*body,*p(2),*p(1))

    *body\ptrParent = @compound()
  Next i
  
  ProcedureReturn *body
EndProcedure


; Create a swing (for all your circus needs ;) )
Procedure createSwing(x.i,y.i,width.i,height.i,center.i = 0,mass.f = 1)
  Protected *body.rigidBody_struct
  
  If center = 0
    center = x + width/2
  EndIf
  
  *body = createBody()
  
  *p(1) = addBodyPointmass(*body,x,y,mass)
  *p(2) = addBodyPointmass(*body,center,y,0)
  *p(3) = addBodyPointmass(*body,x + width,y,mass)
  *p(4) = addBodyPointmass(*body,center,y + height,mass)

  
  addBodyConstraint(*body,*p(1),*p(2))
  addBodyConstraint(*body,*p(2),*p(3))
  addBodyConstraint(*body,*p(2),*p(4),#False)
  addBodyConstraint(*body,*p(1),*p(4))
  addBodyConstraint(*body,*p(3),*p(4))
  
  ProcedureReturn *body  
EndProcedure


; Create a ball
Procedure createBall(x.i,y.i,radius.f,mass.f = 1)
  Protected i.i
  Protected *body.rigidBody_struct
  
  *body = createBody()
  *p(1) = addBodyPointmass(*body,x,y,mass*3)
  For i = 0 To 330 Step 30
    If i = 0
      *p(2) = addBodyPointmass(*body,x+Cos(Radian(i))*radius,y+Sin(Radian(i))*radius,mass)
      *p(5) = *p(2)
    Else
      *p(3) = *p(2)
      *p(2) = addBodyPointmass(*body,x+Cos(Radian(i))*radius,y+Sin(Radian(i))*radius,mass)
      addBodyConstraint(*body,*p(2),*p(3))
      addBodyConstraint(*body,*p(1),*p(3),#False)
    EndIf
  Next i
  addBodyConstraint(*body,*p(2),*p(5))
  addBodyConstraint(*body,*p(1),*p(2),#False)
  
  ProcedureReturn *body  
EndProcedure


; Create a primitive ragdoll
Procedure createRagdoll(x.i,y.i,width.i = 50,height.i = 100,legSupport.b = #False)
  Protected *ptrCompound.compound_struct,*ptrTorso.rigidbody_struct
  Protected unitX.f = width / 5,unitY.i = height / 10
  
  ; Note to self: All this is a bit complicated; Need a way to attach a body to an other more easily...
  
  *ptrCompound = AddElement(compound())
  
  ; Torso
  *ptrTorso = createBody()
  *ptrTorso\ptrParent = *ptrCompound 
  *p(1) = addBodyPointmass(*ptrTorso,x + unitX,y)
  *p(2) = addBodyPointmass(*ptrTorso,x + 4*unitX,y)
  *p(3) = addBodyPointmass(*ptrTorso,x + 1.5*unitX,y + 5*unitY)
  *p(4) = addBodyPointmass(*ptrTorso,x + 3.5*unitX,y + 5*unitY)      

  addBodyConstraint(*ptrTorso,*p(1),*p(2))
  addBodyConstraint(*ptrTorso,*p(1),*p(3))
  addBodyConstraint(*ptrTorso,*p(2),*p(4))
  addBodyConstraint(*ptrTorso,*p(3),*p(4))
  addBodyConstraint(*ptrTorso,*p(1),*p(4),#False)
  addBodyConstraint(*ptrTorso,*p(2),*p(3),#False)
  
  ; Right forearm
  *body = createBody()
  *body\ptrParent = *ptrCompound 
  *p(5) = addBodyPointmass(*body,x,y + 3*unitY,0.5)
  *p(6) = addBodyPointmass(*body,x,y + 6*unitY,0.5)
  addBodyConstraint(*body,*p(5),*p(6))
  addBodyConstraint(*body,*p(6),*p(5))
  
  ; Right arm (we re-use already existing points from the foream and the torso)
  *body = createBody()
  *body\ptrParent = *ptrCompound
  
  *body\nbPoint + 1
  *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
  PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(1) )
  *body\nbPoint + 1
  *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
  PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(5) )
  
  addBodyConstraint(*body,*p(5),*p(1))
  addBodyConstraint(*body,*p(1),*p(5))
  
  ; Left forearm
  *body = createBody()
  *body\ptrParent = *ptrCompound 
  *p(7) = addBodyPointmass(*body,x + 5*unitX,y + 3*unitY,0.5)
  *p(8) = addBodyPointmass(*body,x + 5*unitX,y + 6*unitY,0.5)
  addBodyConstraint(*body,*p(7),*p(8))
  addBodyConstraint(*body,*p(8),*p(7))
  
  ; Left arm (we re-use already existing points from the foream and the torso)
  *body = createBody()
  *body\ptrParent = *ptrCompound 
  
  *body\nbPoint + 1
  *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
  PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(2) )
  *body\nbPoint + 1
  *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
  PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(7) )
  
  addBodyConstraint(*body,*p(7),*p(2))
  addBodyConstraint(*body,*p(2),*p(7))
  
  ; Right leg
  *body = createBody()
  *body\ptrParent = *ptrCompound 
  *p(9) = addBodyPointmass(*body,x + unitX,y + 8*unitY)
  *p(10) = addBodyPointmass(*body,x + unitX,y + 11*unitY)
  addBodyConstraint(*body,*p(9),*p(10))
  addBodyConstraint(*body,*p(10),*p(9))
  
  ; Right thigh (we re-use already existing points from the leg and the lower torso)
  *body = createBody()
  *body\ptrParent = *ptrCompound 
  
  *body\nbPoint + 1
  *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
  PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(3) )
  *body\nbPoint + 1
  *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
  PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(9) )
  
  addBodyConstraint(*body,*p(9),*p(3))
  addBodyConstraint(*body,*p(3),*p(9))
  
 ; Left leg
  *body = createBody()
  *body\ptrParent = *ptrCompound 
  *p(11) = addBodyPointmass(*body,x + 4*unitX,y + 8*unitY)
  *p(12) = addBodyPointmass(*body,x + 4*unitX,y + 11*unitY)
  addBodyConstraint(*body,*p(11),*p(12))
  addBodyConstraint(*body,*p(12),*p(11))
  
  ; Left thigh (we re-use already existing points from the leg and the lower torso)
  *body = createBody()
  *body\ptrParent = *ptrCompound 
  
  *body\nbPoint + 1
  *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
  PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(4) )
  *body\nbPoint + 1
  *body\ptrPointList = ReAllocateMemory(*body\ptrPointList,(*body\nbPoint + 1) * #SIZEOFPTR )
  PokeI(*body\ptrPointList + *body\nbPoint * #SIZEOFPTR , *p(11) )
  
  addBodyConstraint(*body,*p(11),*p(4))
  addBodyConstraint(*body,*p(4),*p(11))
  
  ; Support constraints (without them, the doll can't stand up)
  If legSupport = #True
    createConstraint(*p(9),*p(11),#False)
    createConstraint(*p(10),*p(12),#False)
    createConstraint(*p(9),*p(4),#False)
    createConstraint(*p(10),*p(11),#False)
  EndIf
  
  ProcedureReturn *ptrTorso
EndProcedure
  

; Create some pre-defined objects, for demo's sake)
Procedure createObjects(num.i)
  Protected x.i,i.i,j.i
  
  Select num
    Case 0  ; Reset
      ClearList(compound())
      ClearList(body())
      ClearList(constraint())
      ClearList(pointmass())
      
    Case 1  ; Random boxes
      For i = 1 To 8          
        createbox(i * 75, 0 ,50,50,1,(300 - Random(600)) / 1000.0,(300 - Random(600)) / 1000.0,(300 - Random(600)) / 1000.0)
      Next i
      createBox(200,150,350,70,5)
      
    Case 2 ; Pile o' boxes
      x = Random(700)+20
      For j=1 To 7
        createBox(x,#SCREENHEIGHT - (j * 50),50,50 )
      Next j
      
    Case 3  ; a ball, a line, a triangle
      createLine(250,300 + Random(100),550,300 + Random(100),0)
      createTriangle(350,50,40,60)
      createBall(560,50,25,0.2)
      
    Case 4  ; a catapult
      x = Random(400) + 100
      createSwing(x,450,200,45)
      createBox(x - 16,450,15,50,0)
      createBox(x,410,25,25)
      createBox(x + 150,0,50,50,10)
      
    Case 5  ; Rope
      x = Random(600)+50
      createRope(x,50,x+150,150,8)
      
    Case 6  ; Bridge (just a rope with both ends static)
      *body = createRope(450,250,680,250,10)
      *p(1) = PeekI(*body\ptrPointList + (*body\nbPoint * #SIZEOFPTR))
      *p(1)\invmass = 0
      
    Case 7  ; Ragdoll
      createRagdoll(500,200)
      
      
    Case 8 ; big boxes atop static points
      createLine(250,400,250,490,0)
      createBox(149,350,200,50,1)
      
      createLine(550,400,550,490,0)
      createBox(451,350,200,50,1)
      
    Case 9 ; speed test
      For i = 10 To #SCREENWIDTH-60 Step 60
        For j=1 To 10
          createBox(i,#SCREENHEIGHT - (j * 50),50,50 )
        Next j
      Next i
      
  EndSelect   
EndProcedure


; Deletes the current body and all its points and constraints
Macro DELETEBODY()
  ; Delete body's constraints
  *ptr = body()\ptrConstraintList
  For i = 0 To body()\nbEdge
    ; But first, check if it hasn't been already deleted
    If FindString(listDeleted," "+Str(PeekI(*ptr))+" ",1) = 0
      ChangeCurrentElement(constraint(),PeekI(*ptr))
      DeleteElement(constraint())
      
      ; Store the reference of the deleted constraint
      listDeleted + " "+Str(PeekI(*ptr))+" "
    EndIf          
    *ptr+#SIZEOFPTR      
  Next i
  ; Delete body's points
  *ptr = body()\ptrPointList
  For i = 0 To body()\nbPoint
    ; But first, check if it hasn't been already deleted
    If FindString(listDeleted," "+Str(PeekI(*ptr))+" ",1) = 0
      ChangeCurrentElement(pointmass(),PeekI(*ptr))
      DeleteElement(pointmass())
      
      ; Store the reference of the deleted point
      listDeleted + " "+Str(PeekI(*ptr))+" "
    EndIf          
    *ptr+#SIZEOFPTR      
  Next i
  
  ; Store the reference of the deleted body
  listDeleted +" "+Str(@body())+" "
  
  ; Delete the body
  DeleteElement(body())
EndMacro  


; Deletes all things tied to the point in input => constraints, bodies, compound
Procedure deleteObjectFromPoint(*ptrPoint.pointmass_struct)
  Protected *ptrConst.constraint_struct
  Protected *ptr,i.i
  Protected listDeleted.s
  
  ForEach constraint()
    ; if the constraint contains the point, delete it and its parents (body, compound...)
    If constraint()\p1 = *ptrPoint Or constraint()\p2 = *ptrPoint
      *ptrConst = @constraint()
      If constraint()\ptrParent <> 0 And FindString(listDeleted," "+Str(@body())+" ",1) = 0

        ; Find the parent body
        ChangeCurrentElement(body(),constraint()\ptrParent)        
        
        ; If the body is part of a compound, delete the compound
        If body()\ptrParent <> 0 
          If FindString(listDeleted," "+Str(body()\ptrParent)+" ",1) = 0
            ChangeCurrentElement(compound(),body()\ptrParent)      
            
            ForEach body()
              If body()\ptrParent = @compound()
                DELETEBODY()
              EndIf
            Next body()
            
            ; Store the reference of the deleted compound
            listDeleted + " "+Str(@compound())+" "
            DeleteElement(compound())
          EndIf ; if FindString(listDeleted ...
        Else
          DELETEBODY()
        EndIf ; else (not part of a compound)
        
      EndIf ; if constraint()\ptrParent <> 0...
    EndIf ; if constraint()\p1 = *ptrPoint or...
    
  Next constraint()
  
EndProcedure

DisableExplicit

;********************************************************
;- --- Main program ---
;********************************************************

;- initialization
InitSprite()
InitSprite3D()
InitKeyboard()
InitMouse()

;- Window
OpenWindow(0, 0, 0, #SCREENWIDTH, #SCREENHEIGHT, "Verlet", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0, 0, #SCREENWIDTH,#SCREENHEIGHT, 0, 0, 0,#PB_Screen_SmartSynchronization)

;- --- Main Loop ---
timer = ElapsedMilliseconds()
Repeat
  While WindowEvent() : Wend
  
  ; Sub-sampling => executes the simulation in small steps; slower, but more precise
  For j = 1 To #SUBSAMPLING
    ; Moves the points
    UPDATE_POINTMASSES()
   
    If *mousePoint > 0
      *mousePoint\x = MouseX()
      *mousePoint\y = MouseY()
      *mousePoint\oldX = *mousePoint\x
      *mousePoint\oldY = *mousePoint\y
    EndIf
   
    ; Solves the constraints
    UPDATE_CONSTRAINTS()
    
    ; Solves the collisions
    MANAGE_COLLISIONS()
  Next j 
  
  ;- Keyboard
  ExamineKeyboard()
  If KeyboardPushed(#PB_Key_RightControl)
    Delay(100)
  EndIf
  If KeyboardReleased(#PB_Key_Return)
    drawmode = 1-drawmode
  EndIf
  If KeyboardReleased(#PB_Key_F1)
    createObjects(1)
  EndIf
  If KeyboardReleased(#PB_Key_F2)
    createObjects(2)
  EndIf
  If KeyboardReleased(#PB_Key_F3)
    createObjects(3)
  EndIf
  If KeyboardReleased(#PB_Key_F4)
    createObjects(4)
  EndIf
  If KeyboardReleased(#PB_Key_F5)
    createObjects(5)
  EndIf
  If KeyboardReleased(#PB_Key_F6)
    createObjects(6)
  EndIf
  If KeyboardReleased(#PB_Key_F7)
    createObjects(7)
  EndIf
  If KeyboardReleased(#PB_Key_F8)
    createObjects(8)
  EndIf
  If KeyboardReleased(#PB_Key_F9)
    createObjects(9)
  EndIf
  
  ;- Mouse
  ExamineMouse()
  If mousemode = #False
    If MouseButton(#PB_MouseButton_Left) And ListSize(pointmass()) > 0
      minDistance.f = 999999
      ForEach pointmass()
        distance = (MouseX() - pointmass()\x)*(MouseX() - pointmass()\x) + (MouseY() - pointmass()\y)*(MouseY() - pointmass()\y)
        If distance < minDistance
          *mousePoint = @pointmass()
          minDistance = distance
        EndIf
      Next pointmass()
      If mindistance <= 300
        mousemode = #True
      Else
        *mousePoint = 0
      EndIf
    EndIf
    
    ; Del => delete all objects
    If KeyboardReleased(#PB_Key_Delete)
      createObjects(0)
      numobj=0
    EndIf
    
  Else
    If MouseButton(#PB_MouseButton_Left) = 0
      mousemode = #False
      *mousePoint = 0
    EndIf
    
    ; Del => delete only the picked object
    If KeyboardReleased(#PB_Key_Delete) And ListSize(pointmass()) > 0
      deleteObjectFromPoint(*mousePoint)
      mousemode = #False
      *mousePoint = 0
    EndIf
  EndIf
  
  
  ;- Drawing
  ClearScreen($000001)
  StartDrawing(ScreenOutput())
  If drawmode = #True
    Circle(MouseX(),MouseY(),4,$0000FF)
    drawconstraints()
    drawpointmasses()
    numobj=0
    ForEach body()
      numobj+1
      Box(body()\center\x,body()\center\y,2,2,$FF00FF)
    Next body()
  EndIf
  
  numfps+1
  If ElapsedMilliseconds()-timer >= 1000
    numfpsShown = numfps
    numfps=0
    timer = ElapsedMilliseconds()
  EndIf
  DrawText(0,0,Str(numobj) + " obj. / " + Str(numfpsShown) + "FPS")

  StopDrawing()
  FlipBuffers()
    
Until KeyboardPushed(#PB_Key_Escape)

End
Avatar de l’utilisateur
venom
Messages : 3137
Inscription : jeu. 29/juil./2004 16:33
Localisation : Klyntar
Contact :

Re: Moteur physique 2D ( Verlet )

Message par venom »

chez moi j'ai juste une petite fenetre noir qui s'ouvre dans la quel je peut bouger un point rien de plus. :? :|
Mais sa tourne a 60fps :lol:

j'ai tenter avec directx 7 et opengl pareil.






@++
Windows 10 x64, PureBasic 5.73 x86 & x64
GPU : radeon HD6370M, CPU : p6200 2.13Ghz
Avatar de l’utilisateur
venom
Messages : 3137
Inscription : jeu. 29/juil./2004 16:33
Localisation : Klyntar
Contact :

Re: Moteur physique 2D ( Verlet )

Message par venom »

chez moi j'ai juste une petite fenetre noir qui s'ouvre dans la quel je peut bouger un point rien de plus. :? :|
Mais sa tourne a 60fps :lol:

j'ai tenter avec directx 7 et opengl pareil.


pas moyen d'inclure un exe dans le zip ?



@++
Windows 10 x64, PureBasic 5.73 x86 & x64
GPU : radeon HD6370M, CPU : p6200 2.13Ghz
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Moteur physique 2D ( Verlet )

Message par flaith »

venom a écrit :chez moi j'ai juste une petite fenetre noir qui s'ouvre dans la quel je peut bouger un point rien de plus. :? :|
Mais sa tourne a 60fps :lol:

j'ai tenter avec directx 7 et opengl pareil.


pas moyen d'inclure un exe dans le zip ?



@++
Il faut utiliser les touches de fonctions F1 à F9 pour afficher les objets et les choper à la souris pour les déplacer ;)
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Moteur physique 2D ( Verlet )

Message par flaith »

djes a écrit :Incontestablement l'un des plus beaux codes du forum. Chapeau!
Indeed :mrgreen:
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Moteur physique 2D ( Verlet )

Message par kelebrindae »

Merci, merci, n'en jetez plus!
Et puis, il doit rester des erreurs dans les réactions aux collisions (surtout avec les lignes dont un des points est statique). Mais bon, merci, j'apprécie! :D

@Djes: Hé! Pourquoi ça marche avec toi ?! C'est pô juste... Merci d'avoir posté le code, en tout cas.

@Flaith: oh punaise...
Bon, pour régler le problème chez toi, tu peux virer le clearScreen et mettre un "Box(0,0,#SCREENWIDTH,#SCREENHEIGHT,0)" juste après le StartDrawing, mais décidément, c'est bizarre...
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Moteur physique 2D ( Verlet )

Message par djes »

kelebrindae a écrit :@Djes: Hé! Pourquoi ça marche avec toi ?! C'est pô juste... Merci d'avoir posté le code, en tout cas.
Ca m'est déjà arrivé... Je crois qu'il y a certains codes de caractères reconnus par l'éditeur de PB mais pas par les navigateurs. Quand ça le fait, il suffit de coller le texte dans le bloc-notes (qui va filtrer), avant de le recopier sur le forum.
Répondre