244 lines
8.7 KiB
Go
244 lines
8.7 KiB
Go
package main
|
|
|
|
import (
|
|
"image/color"
|
|
"sync"
|
|
"time"
|
|
|
|
rl "github.com/gen2brain/raylib-go/raylib"
|
|
)
|
|
|
|
const MAXPOINTS = 100000
|
|
const MAXOBSTACLES = 20
|
|
const PI = 3.141592653589793
|
|
|
|
type quad struct {
|
|
lu rl.Vector2
|
|
ru rl.Vector2
|
|
rd rl.Vector2
|
|
ld rl.Vector2
|
|
}
|
|
|
|
func spawnParticlesAsGrid(particleWidth float32, particleHeight float32, particleColumnCount int, particleRowCount int, particleList *[MAXPOINTS]rl.Vector2) {
|
|
var particleLeftCornerPos rl.Vector2 = rl.Vector2{200, 100}
|
|
|
|
for y := 0; y < (particleColumnCount); y++ {
|
|
for x := 0; x < particleRowCount; x++ {
|
|
newParticle := rl.NewVector2(particleWidth*(float32(x)/float32(particleRowCount))+particleLeftCornerPos.X, particleHeight*(float32(y)/float32(particleColumnCount))+particleLeftCornerPos.Y)
|
|
particleList[y*particleRowCount+x] = newParticle
|
|
}
|
|
}
|
|
}
|
|
|
|
func createRectangle(centerPoint rl.Vector2, width float32, height float32, rotation float32) quad {
|
|
rectangle := quad{}
|
|
rectangle.lu = rl.Vector2Add(
|
|
centerPoint,
|
|
rl.Vector2Rotate(rl.Vector2{-0.5 * width, -0.5 * height},
|
|
rotation))
|
|
rectangle.ru = rl.Vector2Add(
|
|
centerPoint,
|
|
rl.Vector2Rotate(rl.Vector2{0.5 * width, -0.5 * height},
|
|
rotation))
|
|
rectangle.rd = rl.Vector2Add(
|
|
centerPoint,
|
|
rl.Vector2Rotate(rl.Vector2{0.5 * width, 0.5 * height},
|
|
rotation))
|
|
rectangle.ld = rl.Vector2Add(
|
|
centerPoint,
|
|
rl.Vector2Rotate(rl.Vector2{-0.5 * width, 0.5 * height},
|
|
rotation))
|
|
return rectangle
|
|
}
|
|
|
|
func drawRec(rectangle *quad) {
|
|
rl.DrawLineV((*rectangle).lu, (*rectangle).ru, rl.Black)
|
|
rl.DrawLineV((*rectangle).ru, (*rectangle).rd, rl.Black)
|
|
rl.DrawLineV((*rectangle).rd, (*rectangle).ld, rl.Black)
|
|
rl.DrawLineV((*rectangle).ld, (*rectangle).lu, rl.Black)
|
|
}
|
|
|
|
func obstacleReflect(particlePos rl.Vector2, particleSpeed *rl.Vector2, obstacles *([MAXOBSTACLES](*quad)), particleRadius *float32, obstacleCount *int) {
|
|
var friction float32 = 0.99
|
|
|
|
for k := 0; k < *obstacleCount; k++ {
|
|
if rl.CheckCollisionCircleLine(particlePos, *particleRadius, (*((*obstacles)[k])).lu, (*((*obstacles)[k])).ru) {
|
|
var floorVector rl.Vector2 = rl.Vector2Subtract((*((*obstacles)[k])).ru, (*((*obstacles)[k])).lu)
|
|
var normalFloor rl.Vector2 = rl.Vector2Normalize(rl.Vector2Rotate(floorVector, -PI/2))
|
|
*particleSpeed = rl.Vector2Scale(rl.Vector2Reflect(*particleSpeed, normalFloor), friction)
|
|
} else if rl.CheckCollisionCircleLine(particlePos, *particleRadius, (*((*obstacles)[k])).ru, (*((*obstacles)[k])).rd) {
|
|
var floorVector rl.Vector2 = rl.Vector2Subtract((*((*obstacles)[k])).rd, (*((*obstacles)[k])).ru)
|
|
var normalFloor rl.Vector2 = rl.Vector2Normalize(rl.Vector2Rotate(floorVector, -PI/2))
|
|
*particleSpeed = rl.Vector2Scale(rl.Vector2Reflect(*particleSpeed, normalFloor), friction)
|
|
} else if rl.CheckCollisionCircleLine(particlePos, *particleRadius, (*((*obstacles)[k])).rd, (*((*obstacles)[k])).ld) {
|
|
var floorVector rl.Vector2 = rl.Vector2Subtract((*((*obstacles)[k])).ld, (*((*obstacles)[k])).rd)
|
|
var normalFloor rl.Vector2 = rl.Vector2Normalize(rl.Vector2Rotate(floorVector, -PI/2))
|
|
*particleSpeed = rl.Vector2Scale(rl.Vector2Reflect(*particleSpeed, normalFloor), friction)
|
|
} else if rl.CheckCollisionCircleLine(particlePos, *particleRadius, (*((*obstacles)[k])).ld, (*((*obstacles)[k])).lu) {
|
|
var floorVector rl.Vector2 = rl.Vector2Subtract((*((*obstacles)[k])).lu, (*((*obstacles)[k])).ld)
|
|
var normalFloor rl.Vector2 = rl.Vector2Normalize(rl.Vector2Rotate(floorVector, -PI/2))
|
|
*particleSpeed = rl.Vector2Scale(rl.Vector2Reflect(*particleSpeed, normalFloor), friction)
|
|
}
|
|
}
|
|
}
|
|
|
|
func physics(particleList *[MAXPOINTS]rl.Vector2, particleCount int, particleListKey *sync.Mutex, obstacles *([MAXOBSTACLES](*quad)), particleRadius *float32, obstacleCount *int, particleColor *[MAXPOINTS]rl.Color) {
|
|
var particleSpeed = [MAXPOINTS]rl.Vector2{{0, -300}} //pixel pro Sekunde
|
|
var timeIncrement float64 = 0.03
|
|
var gravity rl.Vector2 = rl.Vector2{0, 0 * 300}
|
|
var newParticleList [MAXPOINTS]rl.Vector2
|
|
var newParticleSpeed [MAXPOINTS]rl.Vector2
|
|
var particleFriction float32 = 0.65 //0.65
|
|
var particleLadung int = -1
|
|
var masse float32 = 0.1
|
|
var electrostaticRange float32 = 200 //range wiie weit die kraftwirkung ist
|
|
var electroStrength float32 = 2000 //2000
|
|
var hue = [MAXPOINTS]float32{}
|
|
var speedcolor float32 = 30
|
|
|
|
for j := float64(0); j < 50; {
|
|
var startTime time.Time = time.Now()
|
|
|
|
// Snapshot der aktuellen Positionen für den parallelen Block (read-only)
|
|
particleListKey.Lock()
|
|
currentPositions := *particleList
|
|
particleListKey.Unlock()
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for k := 0; k < particleCount; k++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
var electroForce rl.Vector2
|
|
// Jede Goroutine arbeitet nur auf Index i → kein Datenkonflikt
|
|
for l := 0; l < particleCount; l++ {
|
|
if l == i {
|
|
continue
|
|
}
|
|
|
|
diff := rl.Vector2Subtract(currentPositions[i], currentPositions[l])
|
|
dist := rl.Vector2Length(diff)
|
|
minDist := *particleRadius * 2
|
|
|
|
if dist < minDist && dist > 0 {
|
|
colNormal := rl.Vector2Scale(diff, 1/dist) //durch länge des Vektoren teilen
|
|
|
|
// Exakt die Hälfte der Überlappung korrigieren
|
|
overlap := minDist - dist
|
|
currentPositions[i] = rl.Vector2Add(currentPositions[i], rl.Vector2Scale(colNormal, overlap*0.5))
|
|
|
|
particleSpeed[i] = rl.Vector2Add(rl.Vector2Scale(rl.Vector2Reflect(particleSpeed[i], colNormal), particleFriction), rl.Vector2Scale(colNormal, 12))
|
|
}
|
|
if dist < electrostaticRange {
|
|
var tempForce rl.Vector2 = rl.Vector2Scale(diff, -float32(particleLadung)*(electroStrength/(dist*dist*dist)))
|
|
electroForce = rl.Vector2Add(electroForce, tempForce)
|
|
}
|
|
newParticleSpeed[i] = rl.Vector2Add(
|
|
particleSpeed[i],
|
|
rl.Vector2Scale(electroForce, float32(timeIncrement)/masse))
|
|
|
|
}
|
|
|
|
//hue[i]++
|
|
//(*particleColor)[i] = rl.ColorFromHSV(hue[i], 1.0, 1.0)
|
|
hue[i] = rl.Clamp(speedcolor*360/(rl.Vector2Length(newParticleSpeed[i])+1), 1, 360)
|
|
(*particleColor)[i] = rl.ColorFromHSV(hue[i], 1.0, 1.0)
|
|
obstacleReflect(currentPositions[i], &particleSpeed[i], obstacles, particleRadius, obstacleCount)
|
|
|
|
newParticleList[i] = rl.Vector2Add(
|
|
currentPositions[i],
|
|
rl.Vector2Scale(particleSpeed[i], float32(timeIncrement)))
|
|
|
|
newParticleSpeed[i] = rl.Vector2Add(
|
|
rl.Vector2Add(particleSpeed[i], rl.Vector2Scale(gravity, float32(timeIncrement))),
|
|
rl.Vector2Scale(electroForce, float32(timeIncrement)/masse))
|
|
}(k)
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
// Ergebnisse zurückschreiben — nur dieser Block muss gelockt sein
|
|
particleListKey.Lock()
|
|
*particleList = newParticleList
|
|
particleListKey.Unlock()
|
|
//fmt.Println(particleList)
|
|
|
|
particleSpeed = newParticleSpeed
|
|
|
|
j += timeIncrement
|
|
var endTime time.Time = time.Now()
|
|
var elapsed time.Duration = endTime.Sub(startTime)
|
|
var waitTime float64 = timeIncrement - elapsed.Seconds()
|
|
rl.WaitTime(waitTime)
|
|
println((waitTime / timeIncrement))
|
|
//rl.ClearBackground(rl.RayWhite)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
rl.InitWindow(1200, 800, "raylib [core] example - basic window")
|
|
defer rl.CloseWindow()
|
|
|
|
rl.SetTargetFPS(120)
|
|
|
|
rl.BeginDrawing()
|
|
rl.WaitTime(1)
|
|
rl.ClearBackground(rl.RayWhite)
|
|
rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray)
|
|
|
|
rl.EndDrawing()
|
|
|
|
var particleWidth float32 = 700
|
|
var particleHeight float32 = 500
|
|
var particleColumnCount int = 40
|
|
var particleRowCount int = 40
|
|
var particleCount int = particleRowCount * particleColumnCount
|
|
var particleRadius float32 = 5
|
|
var particleColorList [MAXPOINTS]rl.Color
|
|
particleColor := &particleColorList
|
|
var particleListKey sync.Mutex
|
|
var obstacleCount int = 0
|
|
|
|
//var stepSize float32 = 10
|
|
|
|
var particleList = [MAXPOINTS]rl.Vector2{}
|
|
|
|
spawnParticlesAsGrid(particleWidth, particleHeight, particleColumnCount, particleRowCount, &particleList)
|
|
|
|
var floor quad = createRectangle(rl.Vector2{600, 700}, 1200, 100, 0*PI/8)
|
|
var obstacles = [MAXOBSTACLES]*quad{}
|
|
obstacles[0] = &floor
|
|
obstacleCount++
|
|
var hillLeft quad = createRectangle(rl.Vector2{0, 500}, 1200, 100, PI/2)
|
|
obstacles[1] = &hillLeft
|
|
obstacleCount++
|
|
var hillRight quad = createRectangle(rl.Vector2{1200, 500}, 1200, 100, -PI/2)
|
|
obstacles[2] = &hillRight
|
|
obstacleCount++
|
|
var ceiling quad = createRectangle(rl.Vector2{600, 0}, 1200, 100, 0)
|
|
obstacles[3] = &ceiling
|
|
obstacleCount++
|
|
|
|
go physics(&particleList, particleCount, &particleListKey, &obstacles, &particleRadius, &obstacleCount, particleColor)
|
|
|
|
for !rl.WindowShouldClose() {
|
|
rl.ClearBackground(color.RGBA{15, 16, 23, 1})
|
|
|
|
particleListKey.Lock()
|
|
|
|
rl.BeginDrawing()
|
|
for i := 0; i < particleCount; i++ {
|
|
rl.DrawCircleV(particleList[i], particleRadius, (*particleColor)[i])
|
|
}
|
|
|
|
particleListKey.Unlock()
|
|
drawRec(&floor)
|
|
drawRec(&hillLeft)
|
|
drawRec(&hillRight)
|
|
drawRec(&ceiling)
|
|
rl.EndDrawing()
|
|
}
|
|
|
|
}
|