From 80f99228358e8d7ff2a549232f1b7b2f2950f4f7 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 16 Feb 2025 19:50:13 +0300 Subject: [PATCH 001/196] init new-api --- engine.go | 77 +++++++++ examples/new-api/assets/assets.go | 24 +++ examples/new-api/assets/milansheet.png | Bin 0 -> 96455 bytes examples/new-api/assets/star.png | Bin 0 -> 160 bytes examples/new-api/components/components.go | 22 +++ examples/new-api/components/hp.go | 27 ++++ examples/new-api/desktop-components.go | 52 ++++++ examples/new-api/desktop-systems.go | 186 ++++++++++++++++++++++ examples/new-api/desktop.go | 31 ++++ examples/new-api/entities/player.go | 116 ++++++++++++++ examples/new-api/systems/color.go | 44 +++++ examples/new-api/systems/hp.go | 31 ++++ examples/new-api/systems/player.go | 86 ++++++++++ examples/new-api/systems/spawn.go | 89 +++++++++++ pkg/ecs/component.go | 21 +++ pkg/ecs/component_test.go | 2 +- pkg/ecs/ecs.go | 4 +- stdcomponents/animation-player.go | 39 +++++ stdcomponents/animation-state.go | 27 ++++ stdcomponents/components.go | 32 ++++ stdcomponents/flip.go | 27 ++++ stdcomponents/network.go | 28 ++++ stdcomponents/position.go | 27 ++++ stdcomponents/rotation.go | 27 ++++ stdcomponents/scale.go | 27 ++++ stdcomponents/sprite-matrix.go | 41 +++++ stdcomponents/sprite-sheet.go | 35 ++++ stdcomponents/sprite.go | 34 ++++ stdcomponents/texture-render.go | 36 +++++ stdcomponents/tint.go | 28 ++++ stdsystems/animation-player.go | 67 ++++++++ stdsystems/animation-spritematrix.go | 67 ++++++++ stdsystems/asset-library.go | 34 ++++ stdsystems/debug.go | 45 ++++++ stdsystems/network-receive.go | 27 ++++ stdsystems/network-send.go | 86 ++++++++++ stdsystems/network.go | 49 ++++++ stdsystems/render.go | 61 +++++++ stdsystems/texture-render-animation.go | 54 +++++++ stdsystems/texture-render-flip.go | 54 +++++++ stdsystems/texture-render-position.go | 46 ++++++ stdsystems/texture-render-rotation.go | 46 ++++++ stdsystems/texture-render-scale.go | 46 ++++++ stdsystems/texture-render-sprite.go | 95 +++++++++++ stdsystems/texture-render-spritematrix.go | 77 +++++++++ stdsystems/texture-render-spritesheet.go | 64 ++++++++ stdsystems/texture-render-tint.go | 49 ++++++ 47 files changed, 2184 insertions(+), 3 deletions(-) create mode 100644 engine.go create mode 100644 examples/new-api/assets/assets.go create mode 100644 examples/new-api/assets/milansheet.png create mode 100644 examples/new-api/assets/star.png create mode 100644 examples/new-api/components/components.go create mode 100644 examples/new-api/components/hp.go create mode 100644 examples/new-api/desktop-components.go create mode 100644 examples/new-api/desktop-systems.go create mode 100644 examples/new-api/desktop.go create mode 100644 examples/new-api/entities/player.go create mode 100644 examples/new-api/systems/color.go create mode 100644 examples/new-api/systems/hp.go create mode 100644 examples/new-api/systems/player.go create mode 100644 examples/new-api/systems/spawn.go create mode 100644 stdcomponents/animation-player.go create mode 100644 stdcomponents/animation-state.go create mode 100644 stdcomponents/components.go create mode 100644 stdcomponents/flip.go create mode 100644 stdcomponents/network.go create mode 100644 stdcomponents/position.go create mode 100644 stdcomponents/rotation.go create mode 100644 stdcomponents/scale.go create mode 100644 stdcomponents/sprite-matrix.go create mode 100644 stdcomponents/sprite-sheet.go create mode 100644 stdcomponents/sprite.go create mode 100644 stdcomponents/texture-render.go create mode 100644 stdcomponents/tint.go create mode 100644 stdsystems/animation-player.go create mode 100644 stdsystems/animation-spritematrix.go create mode 100644 stdsystems/asset-library.go create mode 100644 stdsystems/debug.go create mode 100644 stdsystems/network-receive.go create mode 100644 stdsystems/network-send.go create mode 100644 stdsystems/network.go create mode 100644 stdsystems/render.go create mode 100644 stdsystems/texture-render-animation.go create mode 100644 stdsystems/texture-render-flip.go create mode 100644 stdsystems/texture-render-position.go create mode 100644 stdsystems/texture-render-rotation.go create mode 100644 stdsystems/texture-render-scale.go create mode 100644 stdsystems/texture-render-sprite.go create mode 100644 stdsystems/texture-render-spritematrix.go create mode 100644 stdsystems/texture-render-spritesheet.go create mode 100644 stdsystems/texture-render-tint.go diff --git a/engine.go b/engine.go new file mode 100644 index 00000000..f8fec438 --- /dev/null +++ b/engine.go @@ -0,0 +1,77 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import ( + "gomp/pkg/ecs" + "time" +) + +type EngineSystems interface { + Init() + Update(dt time.Duration) + FixedUpdate(dt time.Duration) + Destroy() +} + +type Engine[C any, S EngineSystems] struct { + World *ecs.World + Components C + Systems S +} + +func NewEngine[C any, S EngineSystems](world *ecs.World, components C, systems S) *Engine[C, S] { + + newGame := Engine[C, S]{ + World: world, + Components: components, + Systems: systems, + } + + return &newGame +} + +func (g *Engine[C, S]) Run(tickrate uint) { + duration := time.Second / time.Duration(tickrate) + + ticker := time.NewTicker(duration) + defer ticker.Stop() + + var ( + t time.Time + dt time.Duration + fixedDt time.Duration + ) + + g.Systems.Init() + defer g.Systems.Destroy() + + for !g.World.ShouldDestroy() { + needFixedUpdate := true + for needFixedUpdate { + select { + default: + needFixedUpdate = false + case <-ticker.C: + t = time.Now() + g.Systems.FixedUpdate(fixedDt) + fixedDt = time.Since(t) + } + } + t = time.Now() + g.Systems.Update(dt) + dt = time.Since(t) + } +} diff --git a/examples/new-api/assets/assets.go b/examples/new-api/assets/assets.go new file mode 100644 index 00000000..ce4046ed --- /dev/null +++ b/examples/new-api/assets/assets.go @@ -0,0 +1,24 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package assets + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" + "gomp/pkg/ecs" +) + +var Textures = ecs.CreateAssetLibrary( + func(path string) rl.Texture2D { + assert.True(rl.IsWindowReady(), "Window is not initialized") + return rl.LoadTexture(path) + }, + func(path string, asset *rl.Texture2D) { + assert.True(rl.IsWindowReady(), "Window is not initialized") + rl.UnloadTexture(*asset) + }, +) diff --git a/examples/new-api/assets/milansheet.png b/examples/new-api/assets/milansheet.png new file mode 100644 index 0000000000000000000000000000000000000000..745a03b1d14b6b9675c809d85756d080c25cc6ec GIT binary patch literal 96455 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%0AqrSyO;0$^S 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%0DAAG8$oMY|LOa(2xZ}`v*KcRZCug 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%0DAAG8$oMY|LOa(2xZ}`v*KcRZCug z4mMRz5j}6N&OoQaIx3DYXB52&Pu{k#O

#c7XXx8KNK`gtEHYD9R8YzI0qEb6ij1x!xF&*_xUHm zP~6_7Bm4@SJKf>u3o(VQ1osn$l*gYcDH$cD8x%q|o9J*sGzspmQE)@Spxu`1)(c4Q-k)b}$lUE?v z0?j-vmu7Smf(L;=+NiGt#}<)pkT>uT&kzR8R}izKOHM_{Z9mOyw0{yTI5~26If;;^ zdmeJ;$CvkH{DYI}jh_R*#!Cb%eBM+Y?aYpdJhg#f05#&>FE3!xvxh@$x@Pbm;B`48 z_VVRFBfntIH*|rQ;Jc0E7s!K9L(fPqId$~&xOJz?sfV9deEsnyk;&VM6Ji#M?glGd z%CPB5f-*2Di0fL(<#gMiT(2Pyl+#sgiPn!p#f$=wZPF zkWKg2UEYnXeGk^%NKd8maj(J0S5!8meIOYedb-%siQ<@1d(~1_RWZSO?qh4_WrD_`>`KW z`GmKwJ4P65<*h;?7(8Rds1o8~Ml$7$4jy&tR6IICbO~5w<3So3Hpp9m6!v*J|EA>O zBJ^*4!mE+ylS_{gE?QY>Qwj!*B@Q_H`MPq&?}7F)!NNwJI*J0|dI~X|&S7l{ot%d*fUIKful8%+Md2HPnJP%2Bidad! z@+1%d#ZDYRBpUQ^p+gBh%E87eu8;onYnVOW!HFX5~n`_?DZDoNusV@5DO{8_l}rE3$UOvQMMeCcR5 zo9zK#TzX6+2L!q1X(yAdnPW#KkI?uh!LWK*=e~d)6Aub8eh@2#W0R9N*kFAcT5sj2 z1$)*j1S_YPTypg2(RJQyD`Cut$2#1_?}ZFbLBS2t+ieL}&VOwApfL&<8{$*+eR%0R z#rT)IlFk{y{z|YJB_;757I9`LCyyOWg&RD3_H3JfUv6(+aKzL6#o|wq-mQ1yX0-iK zNBYJ`&`K<0HuzU1UPJY3-R8QvWbS;628 zW_N>{zX&PdsOX|zxhAdM?8fS&T0a=7oG{zgkR3)dz4Ji<$@WNjt-A8Xr9&L9Iris8 zFwN`-uKV;bnRVWnl_Zby#x=zkE%C}VL4Kl>?Jnyx_Xf{TnPA7HN4)264W9@InXI6kXIS*Tj%Ta1#Uy~KmD t;tP{o@>+f3gG+~vvcKjizvGwY{{teDR)nxYYGD8X002ovPDHLkV1l~|I|Bd! diff --git a/examples/raylib-ecs/assets/star.png b/examples/raylib-ecs/assets/star.png deleted file mode 100644 index 9fd0431c03e1478f015112fdc010f33d395a6671..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|qC8z3LnNjq zCpa+8jEwwmym`|<3bcj6)78&qol`;+ E0QLkmUjP6A diff --git a/examples/raylib-ecs/components/components.go b/examples/raylib-ecs/components/components.go deleted file mode 100644 index 97826fd8..00000000 --- a/examples/raylib-ecs/components/components.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package components - -import ( - "gomp/pkg/ecs" -) - -// ============ -// Business -// ============ - -var HealthService = ecs.CreateComponentService[Health](HEALTH_ID) - -// ============ -// Default -// ============ - -var PositionService = ecs.CreateComponentService[Position](POSITION_ID) -var RotationService = ecs.CreateComponentService[Rotation](ROTATION_ID) -var ScaleService = ecs.CreateComponentService[Scale](SCALE_ID) -var MirroredService = ecs.CreateComponentService[Mirrored](MIRRORED_ID) - -// Rendering - -var SpriteService = ecs.CreateComponentService[Sprite](SPRITE_ID) -var SpriteSheetService = ecs.CreateComponentService[SpriteSheet](SPRITE_SHEET_ID) -var SpriteMatrixService = ecs.CreateComponentService[SpriteMatrix](SPRITE_MATRIX_ID) -var TintService = ecs.CreateComponentService[Tint](TINT_ID) - -var AnimationPlayerService = ecs.CreateComponentService[AnimationPlayer](ANIMATION_ID) -var AnimationStateService = ecs.CreateComponentService[AnimationState](ANIMATION_STATE_ID) - -var TextureRenderService = ecs.CreateComponentService[TextureRender](TEXTURE_RENDER_ID) - -// Network - -var NetworkComponentService = ecs.CreateComponentService[Network](NETWORK_ID) diff --git a/examples/raylib-ecs/components/ids.go b/examples/raylib-ecs/components/ids.go deleted file mode 100644 index d671e938..00000000 --- a/examples/raylib-ecs/components/ids.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package components - -import ( - "gomp/pkg/ecs" -) - -const ( - INVALID_ID ecs.ComponentID = iota - COLOR_ID - POSITION_ID - ROTATION_ID - SCALE_ID - MIRRORED_ID - HEALTH_ID - DESTROY_ID - SPRITE_ID - SPRITE_SHEET_ID - SPRITE_MATRIX_ID - TEXTURE_RENDER_ID - ANIMATION_ID - ANIMATION_STATE_ID - TINT_ID - SPRITE_ANIMATOR_ID - NETWORK_ID -) diff --git a/examples/raylib-ecs/components/types.go b/examples/raylib-ecs/components/types.go deleted file mode 100644 index c161d96f..00000000 --- a/examples/raylib-ecs/components/types.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package components - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "image/color" - "time" -) - -// Business - -type Health struct { - Hp, MaxHp int32 -} - -// Default - -type Position struct { - X, Y float32 -} -type Rotation struct { - Angle float32 -} -type Scale struct { - X, Y float32 -} -type Mirrored struct { - X, Y bool -} - -// Render - -type Sprite struct { - Texture *rl.Texture2D - Frame rl.Rectangle - Origin rl.Vector2 - Tint color.RGBA -} -type SpriteSheet struct { - Texture *rl.Texture2D - Frame rl.Rectangle - Origin rl.Vector2 - NumOfFrames int32 - FPS int32 - Vertical bool -} -type Tint = color.RGBA -type SpriteMatrixAnimation struct { - Name string - Frame rl.Rectangle - NumOfFrames uint8 - Vertical bool - Loop bool -} -type SpriteMatrix struct { - Texture *rl.Texture2D - Origin rl.Vector2 - FPS int32 - Animations []SpriteMatrixAnimation -} -type TextureRender struct { - Texture *rl.Texture2D - Frame rl.Rectangle - Origin rl.Vector2 - Tint color.RGBA - Dest rl.Rectangle - Rotation float32 -} -type AnimationPlayer struct { - First uint8 - Last uint8 - Current uint8 - Speed float32 - Loop bool - Vertical bool - ElapsedTime time.Duration - FrameDuration time.Duration - State AnimationState - IsInitialized bool -} -type AnimationState int - -// Network - -type NetworkId int32 -type Network struct { - Id NetworkId - PatchIn []byte - PatchOut []byte -} diff --git a/examples/raylib-ecs/entities/player.go b/examples/raylib-ecs/entities/player.go deleted file mode 100644 index aed5cfb2..00000000 --- a/examples/raylib-ecs/entities/player.go +++ /dev/null @@ -1,116 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package entities - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/examples/raylib-ecs/assets" - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -const ( - PlayerStateIdle components.AnimationState = iota - PlayerStateWalk - PlayerStateJump - PlayerStateFall - PlayerStateAttack - PlayerStateHurt - PlayerStateDie -) - -type Player struct { - ecs2.Entity - Position *components.Position - Rotation *components.Rotation - Scale *components.Scale - SpriteMatrix *components.SpriteMatrix - Tint *components.Tint - AnimationPlayer *components.AnimationPlayer - AnimationState *components.AnimationState - Mirrored *components.Mirrored -} - -var playerSpriteMatrix = components.SpriteMatrix{ - Texture: assets.Textures.Get("examples/raylib-ecs/assets/milansheet.png"), - Origin: rl.Vector2{X: 0.5, Y: 0.5}, - FPS: 12, - Animations: []components.SpriteMatrixAnimation{ - { - Name: "idle", - Frame: rl.Rectangle{X: 0, Y: 0, Width: 96, Height: 128}, - NumOfFrames: 1, - Vertical: false, - Loop: true, - }, - { - Name: "walk", - Frame: rl.Rectangle{X: 0, Y: 512, Width: 96, Height: 128}, - NumOfFrames: 8, - Vertical: false, - Loop: true, - }, - { - Name: "jump", - Frame: rl.Rectangle{X: 96, Y: 0, Width: 96, Height: 128}, - NumOfFrames: 1, - Vertical: false, - Loop: false, - }, - }, -} - -func CreatePlayer(world *ecs2.EntityManager) (player Player) { - // Getting managers - spriteMatrixes := components.SpriteMatrixService.GetManager(world) - positions := components.PositionService.GetManager(world) - rotations := components.RotationService.GetManager(world) - scales := components.ScaleService.GetManager(world) - animationPlayers := components.AnimationPlayerService.GetManager(world) - animationStates := components.AnimationStateService.GetManager(world) - tints := components.TintService.GetManager(world) - mirrored := components.MirroredService.GetManager(world) - - // Creating new player - - entity := world.Create("player") - player.Entity = entity - - // Adding position component - t := components.Position{} - player.Position = positions.Create(entity, t) - - // Adding rotation component - rotation := components.Rotation{} - player.Rotation = rotations.Create(entity, rotation) - - // Adding scale component - scale := components.Scale{ - X: 1, - Y: 1, - } - player.Scale = scales.Create(entity, scale) - - // Adding Tint component - tint := components.Tint{R: 255, G: 255, B: 255, A: 255} - player.Tint = tints.Create(entity, tint) - - // Adding sprite matrix component - player.SpriteMatrix = spriteMatrixes.Create(entity, playerSpriteMatrix) - - // Adding animation player component - animation := components.AnimationPlayer{} - player.AnimationPlayer = animationPlayers.Create(entity, animation) - - // Adding Animation state component - player.AnimationState = animationStates.Create(entity, PlayerStateWalk) - - // Adding Mirrored component - player.Mirrored = mirrored.Create(entity, components.Mirrored{}) - - return player -} diff --git a/examples/raylib-ecs/main.go b/examples/raylib-ecs/main.go deleted file mode 100644 index 363116a6..00000000 --- a/examples/raylib-ecs/main.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -<- Монтажер сука Donated 50 RUB - -Thank you for your support! -*/ - -package main - -import ( - "gomp/examples/raylib-ecs/components" - "gomp/examples/raylib-ecs/systems" - "gomp/pkg/ecs" -) - -func main() { - world := ecs.NewEntityManager("main-raylib") - defer world.Destroy() - - // world.ApplyPatch(patch) - - // components.TransformService.OnPatch(func(newstate components.Transform, oldstate components.Transform) components.Transform { - // return new - // }) - - world.RegisterComponentServices( - &components.PositionService, - &components.RotationService, - &components.ScaleService, - &components.MirroredService, - &components.HealthService, - &components.SpriteService, - &components.SpriteSheetService, - &components.SpriteMatrixService, - &components.TintService, - &components.AnimationPlayerService, - &components.AnimationStateService, - &components.TextureRenderService, - ) - - world.RegisterSystems(). - Sequential( // Initial systems: Main thread - &systems.InitService, - ). - Parallel( // Network receive systems - &systems.NetworkService, - &systems.NetworkReceiveService, - ). - Parallel( // Business logic systems - &systems.PlayerService, - &systems.HpService, - &systems.ColorService, - ). - Parallel( - // Network send systems - &systems.NetworkSendService, - // Prerender systems - &systems.AnimationSpriteMatrixService, - &systems.AnimationPlayerService, - &systems.TRSpriteService, - &systems.TRSpriteSheetService, - &systems.TRSpriteMatrixService, - &systems.TRAnimationService, - &systems.TRMirroredService, - &systems.TRPositionService, - &systems.TRRotationService, - &systems.TRScaleService, - &systems.TRTintService, - ). - Sequential( // Render systems: Main thread - &systems.RenderService, - &systems.DebugService, - &systems.AssetLibService, - ) - - world.Run(1) -} diff --git a/examples/raylib-ecs/systems/animation-player.go b/examples/raylib-ecs/systems/animation-player.go deleted file mode 100644 index b88c0164..00000000 --- a/examples/raylib-ecs/systems/animation-player.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "fmt" - "gomp/examples/raylib-ecs/components" - "gomp/pkg/ecs" - "time" - - "github.com/negrel/assert" -) - -type animationPlayerController struct{} - -func (s *animationPlayerController) Init(world *ecs.EntityManager) {} -func (s *animationPlayerController) Update(world *ecs.EntityManager) { - animationPlayers := components.AnimationPlayerService.GetManager(world) - - animationPlayers.AllDataParallel(func(animation *components.AnimationPlayer) bool { - animation.ElapsedTime += time.Duration(float32(world.DtUpdate().Microseconds())*animation.Speed) * time.Microsecond - - assert.True(animation.FrameDuration > 0, fmt.Errorf("frame duration must be greater than 0 (got %v)", animation.FrameDuration)) - - // Check if animation is playing backwards - if animation.Speed < 0 { - for animation.ElapsedTime <= 0 { - animation.ElapsedTime += animation.FrameDuration - animation.Current-- - - if animation.Current < animation.First { - if animation.Loop { - animation.Current = animation.Last - } else { - animation.Current = animation.First - } - } - } - } else { - for animation.ElapsedTime >= animation.FrameDuration { - animation.ElapsedTime -= animation.FrameDuration - animation.Current++ - - if animation.Current > animation.Last { - if animation.Loop { - animation.Current = animation.First - } else { - animation.Current = animation.Last - } - } - } - } - - return true - }) -} -func (s *animationPlayerController) FixedUpdate(world *ecs.EntityManager) {} -func (s *animationPlayerController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/animation-spritematrix.go b/examples/raylib-ecs/systems/animation-spritematrix.go deleted file mode 100644 index b53669e6..00000000 --- a/examples/raylib-ecs/systems/animation-spritematrix.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" - "time" -) - -type animationSpriteMatrixController struct{} - -func (s *animationSpriteMatrixController) Init(world *ecs2.EntityManager) {} -func (s *animationSpriteMatrixController) Update(world *ecs2.EntityManager) { - animationPlayers := components.AnimationPlayerService.GetManager(world) - animationStates := components.AnimationStateService.GetManager(world) - spriteMatrixes := components.SpriteMatrixService.GetManager(world) - - animationPlayers.AllParallel(func(e ecs2.Entity, animationPlayer *components.AnimationPlayer) bool { - spriteMatrix := spriteMatrixes.Get(e) - if spriteMatrix == nil { - return true - } - - animationStatePtr := animationStates.Get(e) - if animationStatePtr == nil { - return true - } - animationState := *animationStatePtr - - if animationPlayer.State == animationState && animationPlayer.IsInitialized == true { - return true - } - - currentAnimation := spriteMatrix.Animations[animationState] - - animationPlayer.First = 0 - animationPlayer.Current = 0 - animationPlayer.Last = currentAnimation.NumOfFrames - 1 - animationPlayer.Loop = currentAnimation.Loop - animationPlayer.Vertical = currentAnimation.Vertical - animationPlayer.FrameDuration = time.Second / time.Duration(spriteMatrix.FPS) - animationPlayer.State = animationState - animationPlayer.Speed = 1 - animationPlayer.IsInitialized = true - - return true - }) -} -func (s *animationSpriteMatrixController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *animationSpriteMatrixController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/asset-library.go b/examples/raylib-ecs/systems/asset-library.go deleted file mode 100644 index bcec13f2..00000000 --- a/examples/raylib-ecs/systems/asset-library.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp" - ecs2 "gomp/pkg/ecs" -) - -type assetLibController struct { - assets []gomp.AnyAssetLibrary -} - -func (s *assetLibController) Init(world *ecs2.EntityManager) {} -func (s *assetLibController) Update(world *ecs2.EntityManager) { - for _, asset := range s.assets { - asset.LoadAll() - } -} -func (s *assetLibController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *assetLibController) Destroy(world *ecs2.EntityManager) { - for _, asset := range s.assets { - asset.UnloadAll() - } -} diff --git a/examples/raylib-ecs/systems/color.go b/examples/raylib-ecs/systems/color.go deleted file mode 100644 index e92eff64..00000000 --- a/examples/raylib-ecs/systems/color.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" - "image/color" -) - -type colorController struct { - baseColor color.RGBA -} - -func (s *colorController) Init(world *ecs2.EntityManager) { - s.baseColor = color.RGBA{25, 220, 200, 255} -} -func (s *colorController) Update(world *ecs2.EntityManager) {} -func (s *colorController) FixedUpdate(world *ecs2.EntityManager) { - sprites := components.SpriteService.GetManager(world) - hps := components.HealthService.GetManager(world) - - sprites.AllParallel(func(entity ecs2.Entity, sprite *components.Sprite) bool { - hp := hps.Get(entity) - if hp == nil { - return true - } - - hpPercentage := float32(hp.Hp) / float32(hp.MaxHp) - - sprite.Tint = color.RGBA{ - uint8(hpPercentage * float32(s.baseColor.R)), - uint8(hpPercentage * float32(s.baseColor.G)), - uint8(hpPercentage * float32(s.baseColor.B)), - s.baseColor.A, - } - return true - }) -} -func (s *colorController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/debug.go b/examples/raylib-ecs/systems/debug.go deleted file mode 100644 index fc4bbf73..00000000 --- a/examples/raylib-ecs/systems/debug.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "fmt" - "gomp/pkg/ecs" - "log" - "os" - "runtime/pprof" - - rl "github.com/gen2brain/raylib-go/raylib" -) - -type debugController struct { - pprofEnabled bool -} - -func (s *debugController) Init(world *ecs.EntityManager) {} -func (s *debugController) Update(world *ecs.EntityManager) { - if rl.IsKeyPressed(rl.KeyF9) { - if s.pprofEnabled { - pprof.StopCPUProfile() - fmt.Println("CPU Profile Stopped") - } else { - f, err := os.Create("cpu.out") - if err != nil { - log.Fatal(err) - } - pprof.StartCPUProfile(f) - fmt.Println("CPU Profile Started") - } - - s.pprofEnabled = !s.pprofEnabled - } -} -func (s *debugController) FixedUpdate(world *ecs.EntityManager) {} -func (s *debugController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/hp.go b/examples/raylib-ecs/systems/hp.go deleted file mode 100644 index c9025b6d..00000000 --- a/examples/raylib-ecs/systems/hp.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -type hpController struct{} - -func (s *hpController) Init(world *ecs2.EntityManager) {} -func (s *hpController) Update(world *ecs2.EntityManager) {} -func (s *hpController) FixedUpdate(world *ecs2.EntityManager) { - healths := components.HealthService.GetManager(world) - - healths.All(func(entity ecs2.Entity, h *components.Health) bool { - h.Hp-- - - if h.Hp <= 0 { - world.Delete(entity) - } - - return true - }) -} -func (s *hpController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/init.go b/examples/raylib-ecs/systems/init.go deleted file mode 100644 index 41a0f11c..00000000 --- a/examples/raylib-ecs/systems/init.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/pkg/ecs" -) - -type initController struct { -} - -func (s *initController) Init(world *ecs.EntityManager) {} - -func (s *initController) Update(world *ecs.EntityManager) {} -func (s *initController) FixedUpdate(world *ecs.EntityManager) {} -func (s *initController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/network-receive.go b/examples/raylib-ecs/systems/network-receive.go deleted file mode 100644 index 7f611453..00000000 --- a/examples/raylib-ecs/systems/network-receive.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package systems - -import ( - "gomp/pkg/ecs" -) - -type networkReceiveController struct{} - -func (s *networkReceiveController) Init(world *ecs.EntityManager) {} -func (s *networkReceiveController) Update(world *ecs.EntityManager) {} -func (s *networkReceiveController) FixedUpdate(world *ecs.EntityManager) {} -func (s *networkReceiveController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/network-send.go b/examples/raylib-ecs/systems/network-send.go deleted file mode 100644 index 2832b3f1..00000000 --- a/examples/raylib-ecs/systems/network-send.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package systems - -import ( - "fmt" - "gomp/examples/raylib-ecs/components" - "gomp/network" - "gomp/pkg/ecs" -) - -type networkSendController struct{} - -func (s *networkSendController) Init(world *ecs.EntityManager) { - positions := components.PositionService.GetManager(world) - rotations := components.RotationService.GetManager(world) - mirroreds := components.MirroredService.GetManager(world) - - positions.TrackChanges = true - rotations.TrackChanges = true - mirroreds.TrackChanges = true - - positions.SetEncoder(func(components []components.Position) []byte { - data := make([]byte, 0) - for _, component := range components { - binary := fmt.Sprintf("%b", component.X) - data = append(data, []byte(binary)...) - } - - return data - }) - rotations.SetEncoder(func(components []components.Rotation) []byte { - data := make([]byte, 0) - for _, component := range components { - binary := fmt.Sprintf("%b", component.Angle) - data = append(data, []byte(binary)...) - } - - return data - }) - mirroreds.SetEncoder(func(components []components.Mirrored) []byte { - data := make([]byte, 0) - for _, component := range components { - binary := fmt.Sprintf("%b", component.X) - data = append(data, []byte(binary)...) - } - - return data - }) -} -func (s *networkSendController) Update(world *ecs.EntityManager) {} -func (s *networkSendController) FixedUpdate(world *ecs.EntityManager) { - //patch := world.PatchGet() - //world.PatchReset() - //log.Printf("%v", patch) - if network.Quic.Mode() != network.ModeNone { - network.Quic.Send([]byte("patch"), 0) - } -} -func (s *networkSendController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/network.go b/examples/raylib-ecs/systems/network.go deleted file mode 100644 index b3fe5c93..00000000 --- a/examples/raylib-ecs/systems/network.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package systems - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/network" - "gomp/pkg/ecs" -) - -type NetworkMode int - -const ( - None NetworkMode = iota - Server - Client -) - -type networkController struct { -} - -func (s *networkController) Init(world *ecs.EntityManager) { -} -func (s *networkController) Update(world *ecs.EntityManager) { - if rl.IsKeyPressed(rl.KeyP) { - network.Quic.Host("127.0.0.1:27015") - } - - if rl.IsKeyPressed(rl.KeyO) { - network.Quic.Connect("127.0.0.1:27015") - } -} -func (s *networkController) FixedUpdate(world *ecs.EntityManager) {} -func (s *networkController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/player.go b/examples/raylib-ecs/systems/player.go deleted file mode 100644 index 8cc6b6cd..00000000 --- a/examples/raylib-ecs/systems/player.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/examples/raylib-ecs/components" - "gomp/examples/raylib-ecs/entities" - "gomp/pkg/ecs" -) - -type playerController struct { - player entities.Player -} - -func (s *playerController) Init(world *ecs.EntityManager) { - s.player = entities.CreatePlayer(world) - s.player.Position.X = 100 - s.player.Position.Y = 100 - -} -func (s *playerController) Update(world *ecs.EntityManager) { - animationStates := components.AnimationStateService.GetManager(world) - - animationState := animationStates.Get(s.player.Entity) - - if rl.IsKeyDown(rl.KeySpace) { - *animationState = entities.PlayerStateJump - } else { - *animationState = entities.PlayerStateIdle - if rl.IsKeyDown(rl.KeyD) { - *animationState = entities.PlayerStateWalk - s.player.Position.X++ - s.player.Mirrored.X = false - } - if rl.IsKeyDown(rl.KeyA) { - *animationState = entities.PlayerStateWalk - s.player.Position.X-- - s.player.Mirrored.X = true - } - if rl.IsKeyDown(rl.KeyW) { - *animationState = entities.PlayerStateWalk - s.player.Position.Y-- - } - if rl.IsKeyDown(rl.KeyS) { - *animationState = entities.PlayerStateWalk - s.player.Position.Y++ - } - } -} -func (s *playerController) FixedUpdate(world *ecs.EntityManager) {} -func (s *playerController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/render.go b/examples/raylib-ecs/systems/render.go deleted file mode 100644 index 6bbeb492..00000000 --- a/examples/raylib-ecs/systems/render.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "fmt" - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/examples/raylib-ecs/components" - "gomp/pkg/ecs" -) - -type renderController struct { - windowWidth, windowHeight int32 -} - -func (s *renderController) Init(world *ecs.EntityManager) { - rl.InitWindow(s.windowWidth, s.windowHeight, "raylib [core] ebiten-ecs - basic window") - currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) - rl.SetTargetFPS(currentMonitorRefreshRate) -} -func (s *renderController) Update(world *ecs.EntityManager) { - spriteRenders := components.TextureRenderService.GetManager(world) - - if rl.WindowShouldClose() { - world.SetShouldDestroy(true) - return - } - - rl.BeginDrawing() - - rl.ClearBackground(rl.Black) - - spriteRenders.AllData(func(tr *components.TextureRender) bool { - texture := *tr.Texture - rl.DrawTexturePro(texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) - return true - }) - - // rl.DrawRectangle(0, 0, 120, 120, rl.DarkGray) - rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d", world.Size()), 10, 30, 20, rl.Red) - rl.DrawText(fmt.Sprintf("%s", world.DtUpdate()), 10, 50, 20, rl.Red) - rl.DrawText(fmt.Sprintf("%s", world.DtFixedUpdate()), 10, 70, 20, rl.Red) - - rl.EndDrawing() -} - -func (s *renderController) FixedUpdate(world *ecs.EntityManager) {} -func (s *renderController) Destroy(world *ecs.EntityManager) { - rl.CloseWindow() -} diff --git a/examples/raylib-ecs/systems/spawn.go b/examples/raylib-ecs/systems/spawn.go deleted file mode 100644 index 6ef47b4c..00000000 --- a/examples/raylib-ecs/systems/spawn.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/assets" - "gomp/examples/raylib-ecs/components" - "gomp/pkg/ecs" - "math/rand" - - rl "github.com/gen2brain/raylib-go/raylib" -) - -type spawnController struct { - pprofEnabled bool -} - -const ( - minHpPercentage = 20 - minMaxHp = 500 - maxMaxHp = 2000 -) - -func (s *spawnController) Init(world *ecs.EntityManager) {} -func (s *spawnController) Update(world *ecs.EntityManager) { - sprites := components.SpriteService.GetManager(world) - healths := components.HealthService.GetManager(world) - positions := components.PositionService.GetManager(world) - rotations := components.RotationService.GetManager(world) - scales := components.ScaleService.GetManager(world) - - if rl.IsKeyDown(rl.KeySpace) { - for range rand.Intn(10000) { - if world.Size() > 100_000_000 { - break - } - - newCreature := world.Create("Creature") - - // Adding position component - t := components.Position{ - X: float32(rand.Int31n(800)), - Y: float32(rand.Int31n(600)), - } - positions.Create(newCreature, t) - - // Adding rotation component - rotation := components.Rotation{ - Angle: float32(rand.Int31n(360)), - } - rotations.Create(newCreature, rotation) - - // Adding scale component - scale := components.Scale{ - X: 2, - Y: 2, - } - scales.Create(newCreature, scale) - - // Adding HP component - maxHp := minMaxHp + rand.Int31n(maxMaxHp-minMaxHp) - hp := int32(float32(maxHp) * float32(minHpPercentage+rand.Int31n(100-minHpPercentage)) / 100) - h := components.Health{ - Hp: hp, - MaxHp: maxHp, - } - healths.Create(newCreature, h) - - texture := assets.Textures.Get("assets/star.png") - - // Adding sprite component - c := components.Sprite{ - Origin: rl.Vector2{X: 0.5, Y: 0.5}, - Texture: texture, - Frame: rl.Rectangle{X: 0, Y: 0, Width: float32(texture.Width), Height: float32(texture.Height)}, - } - - sprites.Create(newCreature, c) - } - } -} -func (s *spawnController) FixedUpdate(world *ecs.EntityManager) { -} - -func (s *spawnController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/systems-engine.go b/examples/raylib-ecs/systems/systems-engine.go deleted file mode 100644 index c28b9a86..00000000 --- a/examples/raylib-ecs/systems/systems-engine.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package systems - -import ( - "gomp" - "gomp/examples/raylib-ecs/assets" - "gomp/pkg/ecs" -) - -// Engine Initial Systems - -var InitService = ecs.CreateSystemService(&initController{}) -var DebugService = ecs.CreateSystemService(&debugController{}) - -// Engine Network systems - -var NetworkService = ecs.CreateSystemService(&networkController{}) -var NetworkSendService = ecs.CreateSystemService(&networkSendController{}) -var NetworkReceiveService = ecs.CreateSystemService(&networkReceiveController{}) - -// Engine Texture PreRender systems - -var AnimationSpriteMatrixService = ecs.CreateSystemService(&animationSpriteMatrixController{}) -var AnimationPlayerService = ecs.CreateSystemService(&animationPlayerController{}, &AnimationSpriteMatrixService) -var TRSpriteService = ecs.CreateSystemService(&trSpriteController{}, &AnimationPlayerService) -var TRSpriteSheetService = ecs.CreateSystemService(&trSpriteSheetController{}, &AnimationPlayerService) -var TRSpriteMatrixService = ecs.CreateSystemService(&trSpriteMatrixController{}, &AnimationPlayerService) -var TRAnimationService = ecs.CreateSystemService(&trAnimationController{}, &TRSpriteService, &TRSpriteSheetService, &TRSpriteMatrixService) -var TRMirroredService = ecs.CreateSystemService(&trMirroredController{}, &TRSpriteService, &TRSpriteSheetService, &TRSpriteMatrixService, &TRAnimationService) -var TRPositionService = ecs.CreateSystemService(&trPositionController{}, &TRSpriteService, &TRSpriteSheetService, &TRSpriteMatrixService) -var TRRotationService = ecs.CreateSystemService(&trRotationController{}, &TRSpriteService, &TRSpriteSheetService, &TRSpriteMatrixService) -var TRScaleService = ecs.CreateSystemService(&trScaleController{}, &TRSpriteService, &TRSpriteSheetService, &TRSpriteMatrixService) -var TRTintService = ecs.CreateSystemService(&trTintController{}, &TRSpriteService, &TRSpriteSheetService, &TRSpriteMatrixService) - -// Engine Render Systems - -var AssetLibService = ecs.CreateSystemService(&assetLibController{ - assets: []gomp.AnyAssetLibrary{assets.Textures}, -}) -var RenderService = ecs.CreateSystemService(&renderController{ - windowWidth: 1024, - windowHeight: 768, -}) diff --git a/examples/raylib-ecs/systems/texture-render-animation.go b/examples/raylib-ecs/systems/texture-render-animation.go deleted file mode 100644 index 2bcea706..00000000 --- a/examples/raylib-ecs/systems/texture-render-animation.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderPosition is a system that sets Position of textureRender -type trAnimationController struct{} - -func (s *trAnimationController) Init(world *ecs2.EntityManager) {} -func (s *trAnimationController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trAnimationController) Update(world *ecs2.EntityManager) { - // Get component managers - animations := components.AnimationPlayerService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - - // Update sprites and spriteRenders - textureRenders.AllParallel(func(entity ecs2.Entity, tr *components.TextureRender) bool { - if tr == nil { - return true - } - - animation := animations.Get(entity) - if animation == nil { - return true - } - - frame := &tr.Frame - if animation.Vertical { - frame.Y += frame.Height * float32(animation.Current) - } else { - frame.X += frame.Width * float32(animation.Current) - } - - return true - }) -} -func (s *trAnimationController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-mirrored.go b/examples/raylib-ecs/systems/texture-render-mirrored.go deleted file mode 100644 index e8526e83..00000000 --- a/examples/raylib-ecs/systems/texture-render-mirrored.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderScale is a system that sets Scale of textureRender -type trMirroredController struct{} - -func (s *trMirroredController) Init(world *ecs2.EntityManager) {} -func (s *trMirroredController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trMirroredController) Update(world *ecs2.EntityManager) { - // Get component managers - mirroreds := components.MirroredService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - - // Update sprites and spriteRenders - textureRenders.AllParallel(func(entity ecs2.Entity, tr *components.TextureRender) bool { - if tr == nil { - return true - } - - mirrored := mirroreds.Get(entity) - if mirrored == nil { - return true - } - - if mirrored.X { - tr.Frame.Width *= -1 - } - if mirrored.Y { - tr.Frame.Height *= -1 - } - - return true - }) -} -func (s *trMirroredController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-position.go b/examples/raylib-ecs/systems/texture-render-position.go deleted file mode 100644 index a29ea4be..00000000 --- a/examples/raylib-ecs/systems/texture-render-position.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderPosition is a system that sets Position of textureRender -type trPositionController struct{} - -func (s *trPositionController) Init(world *ecs2.EntityManager) {} -func (s *trPositionController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trPositionController) Update(world *ecs2.EntityManager) { - // Get component managers - positions := components.PositionService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - - // Update sprites and spriteRenders - textureRenders.AllParallel(func(entity ecs2.Entity, tr *components.TextureRender) bool { - if tr == nil { - return true - } - - position := positions.Get(entity) - if position == nil { - return true - } - - tr.Dest.X = position.X - tr.Dest.Y = position.Y - - return true - }) -} -func (s *trPositionController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-rotation.go b/examples/raylib-ecs/systems/texture-render-rotation.go deleted file mode 100644 index 6dd91037..00000000 --- a/examples/raylib-ecs/systems/texture-render-rotation.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderRotation is a system that sets Rotation of textureRender -type trRotationController struct{} - -func (s *trRotationController) Init(world *ecs2.EntityManager) {} -func (s *trRotationController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trRotationController) Update(world *ecs2.EntityManager) { - // Get component managers - rotations := components.RotationService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - - // Update sprites and spriteRenders - textureRenders.AllParallel(func(entity ecs2.Entity, tr *components.TextureRender) bool { - if tr == nil { - return true - } - - rotation := rotations.Get(entity) - if rotation == nil { - return true - } - - tr.Rotation = rotation.Angle - - return true - }) -} -func (s *trRotationController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-scale.go b/examples/raylib-ecs/systems/texture-render-scale.go deleted file mode 100644 index b415b0bb..00000000 --- a/examples/raylib-ecs/systems/texture-render-scale.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderScale is a system that sets Scale of textureRender -type trScaleController struct{} - -func (s *trScaleController) Init(world *ecs2.EntityManager) {} -func (s *trScaleController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trScaleController) Update(world *ecs2.EntityManager) { - // Get component managers - scales := components.ScaleService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - - // Update sprites and spriteRenders - textureRenders.AllParallel(func(entity ecs2.Entity, tr *components.TextureRender) bool { - if tr == nil { - return true - } - - scale := scales.Get(entity) - if scale == nil { - return true - } - - tr.Dest.Width *= scale.X - tr.Dest.Height *= scale.Y - - return true - }) -} -func (s *trScaleController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-sprite.go b/examples/raylib-ecs/systems/texture-render-sprite.go deleted file mode 100644 index 79b0f506..00000000 --- a/examples/raylib-ecs/systems/texture-render-sprite.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file deveopment: --===-===-===-===-===-===-===-===-===-=== - -<- Монтажер сука Donated 50 RUB - -Thank you for your support! -*/ - -package systems - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderSprite is a system that prepares Sprite to be rendered -type trSpriteController struct{} - -func (s *trSpriteController) Init(world *ecs2.EntityManager) {} -func (s *trSpriteController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trSpriteController) Update(world *ecs2.EntityManager) { - // Get component managers - sprites := components.SpriteService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - - // Update sprites and spriteRenders - sprites.AllParallel(func(entity ecs2.Entity, sprite *components.Sprite) bool { - if sprite == nil { - return true - } - - spriteFrame := sprite.Frame - spriteOrigin := sprite.Origin - spriteTint := sprite.Tint - - tr := textureRenders.Get(entity) - if tr == nil { - // Create new spriteRender - newRender := components.TextureRender{ - Texture: sprite.Texture, - Frame: sprite.Frame, - Origin: sprite.Origin, - Tint: sprite.Tint, - Dest: rl.NewRectangle( - 0, - 0, - sprite.Frame.Width, - sprite.Frame.Height, - ), - } - - textureRenders.Create(entity, newRender) - } else { - // Update spriteRender - // tr.Texture = sprite.Texture - trFrame := &tr.Frame - trFrame.X = spriteFrame.X - trFrame.Y = spriteFrame.Y - trFrame.Width = spriteFrame.Width - trFrame.Height = spriteFrame.Height - - trOrigin := &tr.Origin - trOrigin.X = spriteOrigin.X - trOrigin.Y = spriteOrigin.Y - - trTint := &tr.Tint - trTint.A = spriteTint.A - trTint.R = spriteTint.R - trTint.G = spriteTint.G - trTint.B = spriteTint.B - - trDest := &tr.Dest - trDest.Width = spriteFrame.Width - trDest.Height = spriteFrame.Height - } - return true - }) -} -func (s *trSpriteController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-spritematrix.go b/examples/raylib-ecs/systems/texture-render-spritematrix.go deleted file mode 100644 index ae0e5568..00000000 --- a/examples/raylib-ecs/systems/texture-render-spritematrix.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderSprite is a system that prepares SpriteSheet to be rendered -type trSpriteMatrixController struct{} - -func (s *trSpriteMatrixController) Init(world *ecs2.EntityManager) {} -func (s *trSpriteMatrixController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trSpriteMatrixController) Update(world *ecs2.EntityManager) { - // Get component managers - spriteMatrixes := components.SpriteMatrixService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - animationStates := components.AnimationStateService.GetManager(world) - - // Update sprites and spriteRenders - spriteMatrixes.AllParallel(func(entity ecs2.Entity, spriteMatrix *components.SpriteMatrix) bool { - if spriteMatrix == nil { - return true - } - - animationState := animationStates.Get(entity) - if animationState == nil { - return true - } - - currentAnimationFrame := spriteMatrix.Animations[*animationState].Frame - - tr := textureRenders.Get(entity) - if tr == nil { - // Create new spriteRender - newRender := components.TextureRender{ - Texture: spriteMatrix.Texture, - Origin: spriteMatrix.Origin, - Frame: currentAnimationFrame, - Dest: rl.Rectangle{ - Width: currentAnimationFrame.Width, - Height: currentAnimationFrame.Height, - }, - } - - textureRenders.Create(entity, newRender) - } else { - // Update spriteRender - tr.Texture = spriteMatrix.Texture - tr.Origin = spriteMatrix.Origin - tr.Dest = rl.Rectangle{ - Width: currentAnimationFrame.Width, - Height: currentAnimationFrame.Height, - } - tr.Frame = currentAnimationFrame - } - return true - }) -} -func (s *trSpriteMatrixController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-spritesheet.go b/examples/raylib-ecs/systems/texture-render-spritesheet.go deleted file mode 100644 index 0ef84aaf..00000000 --- a/examples/raylib-ecs/systems/texture-render-spritesheet.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderSprite is a system that prepares SpriteSheet to be rendered -type trSpriteSheetController struct{} - -func (s *trSpriteSheetController) Init(world *ecs2.EntityManager) {} -func (s *trSpriteSheetController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trSpriteSheetController) Update(world *ecs2.EntityManager) { - // Get component managers - spriteSheets := components.SpriteSheetService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - - // Update sprites and spriteRenders - spriteSheets.AllParallel(func(entity ecs2.Entity, spriteSheet *components.SpriteSheet) bool { - if spriteSheet == nil { - return true - } - - tr := textureRenders.Get(entity) - if tr == nil { - // Create new spriteRender - newRender := components.TextureRender{ - Texture: spriteSheet.Texture, - Frame: spriteSheet.Frame, - Origin: spriteSheet.Origin, - Dest: rl.NewRectangle( - 0, - 0, - spriteSheet.Frame.Width, - spriteSheet.Frame.Height, - ), - } - - textureRenders.Create(entity, newRender) - } else { - // Update spriteRender - tr.Texture = spriteSheet.Texture - tr.Frame = spriteSheet.Frame - tr.Origin = spriteSheet.Origin - } - return true - }) -} -func (s *trSpriteSheetController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-tint.go b/examples/raylib-ecs/systems/texture-render-tint.go deleted file mode 100644 index d8928ee3..00000000 --- a/examples/raylib-ecs/systems/texture-render-tint.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" -) - -// TextureRenderScale is a system that sets Scale of textureRender -type trTintController struct{} - -func (s *trTintController) Init(world *ecs2.EntityManager) {} -func (s *trTintController) FixedUpdate(world *ecs2.EntityManager) {} -func (s *trTintController) Update(world *ecs2.EntityManager) { - // Get component managers - tints := components.TintService.GetManager(world) - textureRenders := components.TextureRenderService.GetManager(world) - - // Update sprites and spriteRenders - textureRenders.AllParallel(func(entity ecs2.Entity, tr *components.TextureRender) bool { - if tr == nil { - return true - } - - tint := tints.Get(entity) - if tint == nil { - return true - } - - trTint := &tr.Tint - trTint.A = tint.A - trTint.R = tint.R - trTint.G = tint.G - trTint.B = tint.B - - return true - }) -} -func (s *trTintController) Destroy(world *ecs2.EntityManager) {} diff --git a/go.mod b/go.mod index 2d596b51..2c05bf89 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module gomp -go 1.24.0 +go 1.23.5 replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go v1.1.7 diff --git a/internal/tomb-mates-demo/main.go b/internal/tomb-mates-demo/main.go index 014cb1f7..27930efe 100644 --- a/internal/tomb-mates-demo/main.go +++ b/internal/tomb-mates-demo/main.go @@ -172,7 +172,7 @@ package tomb_mates_demo // // } // g.Mx.Lock() -// components.Render.Each(g.World, func(e *ecs.Entry) { +// components.Render.Each(g.EntityManager, func(e *ecs.Entry) { // body := components.Transform.GetValue(e) // op.GeoM.Reset() @@ -180,7 +180,7 @@ package tomb_mates_demo // screen.DrawImage(dotBlue, op) // }) -// components.NetworkEntity.Each(g.World, func(e *ecs.Entry) { +// components.NetworkEntity.Each(g.EntityManager, func(e *ecs.Entry) { // ne := components.NetworkEntity.GetValue(e) // op.GeoM.Reset() diff --git a/pkg/ecs/bit-array.go b/pkg/ecs/bit-array.go index 2343d0b7..9d2e56fe 100644 --- a/pkg/ecs/bit-array.go +++ b/pkg/ecs/bit-array.go @@ -52,34 +52,34 @@ const bit_array_size = 256 / bits.UintSize type ComponentBitArray256 [bit_array_size]uint // Set sets the bit at the given index to 1. -func (b *ComponentBitArray256) Set(index ComponentID) { +func (b *ComponentBitArray256) Set(index ComponentId) { b[index/bits.UintSize] |= 1 << (index % bits.UintSize) } // Unset clears the bit at the given index (sets it to 0). -func (b *ComponentBitArray256) Unset(index ComponentID) { +func (b *ComponentBitArray256) Unset(index ComponentId) { b[index/bits.UintSize] &^= 1 << (index % bits.UintSize) } // Toggle toggles the bit at the given index. -func (b *ComponentBitArray256) Toggle(index ComponentID) { +func (b *ComponentBitArray256) Toggle(index ComponentId) { b[index/bits.UintSize] ^= 1 << (index % bits.UintSize) } // IsSet checks if the bit at the given index is set (1). Automatically resizes if the index is out of bounds. -func (b *ComponentBitArray256) IsSet(index ComponentID) bool { +func (b *ComponentBitArray256) IsSet(index ComponentId) bool { return (b[index/bits.UintSize] & (1 << (index % bits.UintSize))) != 0 } -func (b *ComponentBitArray256) AllSet(yield func(ComponentID) bool) { - var id ComponentID +func (b *ComponentBitArray256) AllSet(yield func(ComponentId) bool) { + var id ComponentId var raisedBitsCount int for i, v := range b { raisedBitsCount = bits.OnesCount(v) for range raisedBitsCount { index := bits.Len(v) - 1 v &^= 1 << index - id = ComponentID(i*bits.UintSize + index) + id = ComponentId(i*bits.UintSize + index) if !yield(id) { return } diff --git a/pkg/ecs/bit-set.go b/pkg/ecs/bit-set.go new file mode 100644 index 00000000..8e3fcc6d --- /dev/null +++ b/pkg/ecs/bit-set.go @@ -0,0 +1,144 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "github.com/negrel/assert" + "math/big" + "math/bits" +) + +const ComponentBitsetPreallocate = 1024 + +func NewComponentBitSet() ComponentBitSet { + return ComponentBitSet{ + bits: make([]BitSet, 0, ComponentBitsetPreallocate), + entities: make([]Entity, 0, ComponentBitsetPreallocate), + lookup: make(map[Entity]int, ComponentBitsetPreallocate), + } +} + +const ( + a = 1243123123 & 0 +) + +type BitSet = big.Int + +type ComponentBitSet struct { + bits []BitSet + entities []Entity + lookup map[Entity]int +} + +func (b *ComponentBitSet) Get(entity Entity) BitSet { + bitsId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + return b.bits[bitsId] +} + +// Set sets the bit at the given index to 1. +func (b *ComponentBitSet) Set(entity Entity, componentId ComponentId) { + bitsId, ok := b.lookup[entity] + if !ok { + bitsId = len(b.bits) + b.lookup[entity] = bitsId + b.entities = append(b.entities, entity) + b.bits = append(b.bits, BitSet{}) + } + + bitSet := &b.bits[bitsId] + bitSet.SetBit(bitSet, int(componentId), 1) +} + +// Unset clears the bit at the given index (sets it to 0). +func (b *ComponentBitSet) Unset(entity Entity, componentId ComponentId) { + bitsId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + bitSet := &b.bits[bitsId] + bitSet.SetBit(bitSet, int(componentId), 0) +} + +// Toggle toggles the bit at the given index. +func (b *ComponentBitSet) Toggle(entity Entity, componentId ComponentId) { + bitsId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + bitSet := &b.bits[bitsId] + bitSet.SetBit(bitSet, int(componentId), 1-bitSet.Bit(int(componentId))) +} + +// IsSet checks if the bit at the given index is set (1). Automatically resizes if the index is out of bounds. +func (b *ComponentBitSet) IsSet(entity Entity, componentId ComponentId) bool { + bitsId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + bitSet := &b.bits[bitsId] + + return bitSet.Bit(int(componentId)) == 1 +} + +func (b *ComponentBitSet) Delete(entity Entity) { + bitsId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + + lastIndex := len(b.bits) - 1 + if bitsId < lastIndex { + // swap the dead element with the last one + b.bits[bitsId], b.bits[lastIndex] = b.bits[lastIndex], b.bits[bitsId] + b.entities[bitsId] = b.entities[lastIndex] + + // update lookup table + b.lookup[b.entities[bitsId]] = bitsId + } + + b.bits = b.bits[:lastIndex] + b.entities = b.entities[:lastIndex] + delete(b.lookup, entity) +} + +func (b *ComponentBitSet) FilterByMask(mask BitSet, yield func(entity Entity) bool) { + bitsLen := len(b.bits) + var cmpBitset BitSet + var zeroBitSet BitSet + + for i := range bitsLen { + bitSet := &b.bits[i] + if cmpBitset.And(&cmpBitset, &zeroBitSet).And(bitSet, &mask).Cmp(&mask) != 0 { + continue + } + + if !yield(b.entities[i]) { + return + } + } +} + +func (b *ComponentBitSet) AllSet(entity Entity, yield func(ComponentId) bool) { + bitsId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + + bitSet := b.bits[bitsId].Bits() + var id ComponentId + + for i := range bitSet { + v := uint(bitSet[i]) + for v != 0 { + index := bits.TrailingZeros(v) + v &^= 1 << index + id = ComponentId(i*bits.UintSize + index) + if !yield(id) { + return + } + } + } +} diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 2c78f70a..7c1fd735 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -7,7 +7,6 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs import ( - "math/big" "sync" "github.com/negrel/assert" @@ -17,9 +16,12 @@ import ( // Contracts // ================ +type ComponentId uint +type AnyComponentList interface{} +type AnyComponentListPtr interface{} + type AnyComponentManagerPtr interface { - registerComponentMask(mask *ComponentManager[big.Int]) - getId() ComponentID + Id() ComponentId Remove(Entity) Clean() Has(Entity) bool @@ -28,21 +30,19 @@ type AnyComponentManagerPtr interface { PatchApply(patch ComponentPatch) PatchReset() IsTrackingChanges() bool + registerEntityManager(*EntityManager) } // ================ // Service // ================ -func NewComponentManager[T any](entityManager *EntityManager, id ComponentID) ComponentManager[T] { +func NewComponentManager[T any](id ComponentId) ComponentManager[T] { newManager := ComponentManager[T]{ - mx: new(sync.Mutex), - components: NewPagedArray[T](), entities: NewPagedArray[Entity](), lookup: NewPagedMap[Entity, int32](), - maskComponent: entityManager.entityComponentMask, id: id, isInitialized: true, @@ -56,14 +56,16 @@ func NewComponentManager[T any](entityManager *EntityManager, id ComponentID) Co } type ComponentManager[T any] struct { - mx *sync.Mutex + mx sync.Mutex components *PagedArray[T] entities *PagedArray[Entity] lookup *PagedMap[Entity, int32] - maskComponent *SparseSet[ComponentBitArray256, Entity] - id ComponentID + entityManager *EntityManager + entityComponentBitSet *ComponentBitSet + + id ComponentId isInitialized bool // Patch @@ -86,17 +88,19 @@ type ComponentChanges struct { // ComponentPatch with byte encoded Created, Patched and Deleted components type ComponentPatch struct { - ID ComponentID + ID ComponentId Created ComponentChanges Patched ComponentChanges Deleted ComponentChanges } -func (c *ComponentManager[T]) getId() ComponentID { +func (c *ComponentManager[T]) Id() ComponentId { return c.id } -func (c *ComponentManager[T]) registerComponentMask(*ComponentManager[big.Int]) { +func (c *ComponentManager[T]) registerEntityManager(entityManager *EntityManager) { + c.entityManager = entityManager + c.entityComponentBitSet = &entityManager.componentBitSet } //===================================== @@ -118,8 +122,7 @@ func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { c.entities.Append(entity) component = c.components.Append(value) - mask := c.maskComponent.GetPtr(entity) - mask.Set(c.id) + c.entityComponentBitSet.Set(entity, c.id) c.createdEntities.Append(entity) @@ -178,8 +181,7 @@ func (c *ComponentManager[T]) Remove(entity Entity) { c.entities.SoftReduce() c.lookup.Delete(entity) - mask := c.maskComponent.GetPtr(entity) - mask.Unset(c.id) + c.entityComponentBitSet.Unset(entity, c.id) c.deletedEntities.Append(entity) @@ -357,7 +359,7 @@ func (c *ComponentManager[T]) Len() int32 { } func (c *ComponentManager[T]) Clean() { - c.maskComponent.Clean() + // c.entityComponentBitSet.Clean() // c.components.Clean() // c.entities.Clean() } diff --git a/pkg/ecs/component-service.go b/pkg/ecs/component-service.go deleted file mode 100644 index 634b998c..00000000 --- a/pkg/ecs/component-service.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package ecs - -import ( - "fmt" - "github.com/negrel/assert" - "sync" -) - -type AnyComponentServicePtr interface { - register(*EntityManager) AnyComponentManagerPtr - getId() ComponentID -} - -type ComponentService[T any] struct { - id ComponentID - managers map[*EntityManager]*ComponentManager[T] -} - -func (c *ComponentService[T]) GetManager(world *EntityManager) *ComponentManager[T] { - manager, ok := c.managers[world] - assert.True(ok, fmt.Sprintf("Component <%T> is not registered in world", c)) - return manager -} - -func (c *ComponentService[T]) register(world *EntityManager) AnyComponentManagerPtr { - newManager := ComponentManager[T]{ - mx: new(sync.Mutex), - - components: NewPagedArray[T](), - entities: NewPagedArray[Entity](), - lookup: NewPagedMap[Entity, int32](), - - maskComponent: world.entityComponentMask, - id: c.id, - isInitialized: true, - - TrackChanges: false, - createdEntities: NewPagedArray[Entity](), - patchedEntities: NewPagedArray[Entity](), - deletedEntities: NewPagedArray[Entity](), - } - - c.managers[world] = &newManager - - return &newManager -} - -func (c *ComponentService[T]) getId() ComponentID { - return c.id -} diff --git a/pkg/ecs/ecs.go b/pkg/ecs/ecs.go index 397f0d66..3c30b148 100644 --- a/pkg/ecs/ecs.go +++ b/pkg/ecs/ecs.go @@ -5,28 +5,3 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package ecs - -const ( - PREALLOC_DELETED_ENTITIES uint32 = 1 << 10 -) - -func NewEntityManager() EntityManager { - maskSet := NewSparseSet[ComponentBitArray256, Entity]() - - entityManager := EntityManager{ - deletedEntityIDs: make([]Entity, 0, PREALLOC_DELETED_ENTITIES), - components: make(map[ComponentID]AnyComponentManagerPtr), - entityComponentMask: &maskSet, - } - - return entityManager -} - -func CreateComponentService[T any](id ComponentID) ComponentService[T] { - component := ComponentService[T]{ - id: id, - managers: make(map[*EntityManager]*ComponentManager[T]), - } - - return component -} diff --git a/pkg/ecs/entity-manager.go b/pkg/ecs/entity-manager.go index 4c2400e5..98a71340 100644 --- a/pkg/ecs/entity-manager.go +++ b/pkg/ecs/entity-manager.go @@ -43,85 +43,87 @@ import ( "sync/atomic" ) +const ( + PREALLOC_DELETED_ENTITIES uint32 = 1 << 10 +) + +func NewEntityManager() EntityManager { + entityManager := EntityManager{ + deletedEntityIDs: make([]Entity, 0, PREALLOC_DELETED_ENTITIES), + components: make(map[ComponentId]AnyComponentManagerPtr), + } + + return entityManager +} + type EntityManager struct { lastId Entity size uint32 - components map[ComponentID]AnyComponentManagerPtr - deletedEntityIDs []Entity - entityComponentMask *SparseSet[ComponentBitArray256, Entity] - mx sync.Mutex + groups map[string][]Entity + components map[ComponentId]AnyComponentManagerPtr + deletedEntityIDs []Entity + componentBitSet ComponentBitSet + mx sync.Mutex patch Patch } type Patch []ComponentPatch -func (w *EntityManager) RegisterComponentServices(componentPtr ...AnyComponentServicePtr) { - for i := 0; i < len(componentPtr); i++ { - w.components[componentPtr[i].getId()] = componentPtr[i].register(w) - } -} - -func (w *EntityManager) Create() Entity { - var newId = w.generateEntityID() +func (e *EntityManager) Create() Entity { + var newId = e.generateEntityID() - w.entityComponentMask.Set(newId, ComponentBitArray256{}) - w.size++ + e.size++ return newId } -func (w *EntityManager) Delete(entity Entity) { - mask := w.entityComponentMask.GetPtr(entity) - if mask == nil { - panic(fmt.Sprintf("Entity %d does not exist", entity)) - } +func (e *EntityManager) Delete(entity Entity) { + e.componentBitSet.AllSet(entity, func(id ComponentId) bool { + e.components[id].Remove(entity) + return true + }) - for i := range mask.AllSet { - w.components[i].Remove(entity) - } - - w.entityComponentMask.SoftDelete(entity) - w.deletedEntityIDs = append(w.deletedEntityIDs, entity) - w.size-- + e.deletedEntityIDs = append(e.deletedEntityIDs, entity) + e.size-- } -func (w *EntityManager) Clean() { - for i := range w.components { - w.components[i].Clean() +func (e *EntityManager) Clean() { + for i := range e.components { + e.components[i].Clean() } } -func (w *EntityManager) Size() uint32 { - return w.size +func (e *EntityManager) Size() uint32 { + return e.size } -func (w *EntityManager) LastId() Entity { - return w.lastId +func (e *EntityManager) LastId() Entity { + return e.lastId } -func (w *EntityManager) Destroy() { - w.Clean() +func (e *EntityManager) Destroy() { + e.Clean() } -func (w *EntityManager) PatchGet() Patch { - patch := w.patch +func (e *EntityManager) PatchGet() Patch { + patch := e.patch - for _, component := range w.components { + for _, component := range e.components { if !component.IsTrackingChanges() { continue } - w.patch[component.getId()] = component.PatchGet() + e.patch[component.Id()] = component.PatchGet() } return patch } -func (w *EntityManager) PatchApply(patch Patch) { +func (e *EntityManager) PatchApply(patch Patch) { for _, componentPatch := range patch { - component := w.components[componentPatch.ID] + component := e.components[componentPatch.ID] if component == nil { panic(fmt.Sprintf("Component %d does not exist", componentPatch.ID)) } @@ -134,8 +136,8 @@ func (w *EntityManager) PatchApply(patch Patch) { } } -func (w *EntityManager) PatchReset() { - for i, component := range w.components { +func (e *EntityManager) PatchReset() { + for i, component := range e.components { if component == nil { panic(fmt.Sprintf("Component %d does not exist", i)) } @@ -148,16 +150,21 @@ func (w *EntityManager) PatchReset() { } } -func (w *EntityManager) init() { - w.patch = make(Patch, len(w.components)) +func (e *EntityManager) init() { + e.componentBitSet = NewComponentBitSet() + e.patch = make(Patch, len(e.components)) } -func (w *EntityManager) generateEntityID() (newId Entity) { - if len(w.deletedEntityIDs) == 0 { - newId = Entity(atomic.AddUint32((*entityType)(&w.lastId), 1)) +func (e *EntityManager) generateEntityID() (newId Entity) { + if len(e.deletedEntityIDs) == 0 { + newId = Entity(atomic.AddUint32((*entityType)(&e.lastId), 1)) } else { - newId = w.deletedEntityIDs[len(w.deletedEntityIDs)-1] - w.deletedEntityIDs = w.deletedEntityIDs[:len(w.deletedEntityIDs)-1] + newId = e.deletedEntityIDs[len(e.deletedEntityIDs)-1] + e.deletedEntityIDs = e.deletedEntityIDs[:len(e.deletedEntityIDs)-1] } return newId } + +func (e *EntityManager) registerComponent(c AnyComponentManagerPtr) { + e.components[c.Id()] = c +} diff --git a/pkg/ecs/types.go b/pkg/ecs/entity.go similarity index 74% rename from pkg/ecs/types.go rename to pkg/ecs/entity.go index 2e1a3418..5b60f3c1 100644 --- a/pkg/ecs/types.go +++ b/pkg/ecs/entity.go @@ -2,21 +2,26 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! */ package ecs import "github.com/negrel/assert" -type Mask uint64 -type entityType = uint32 -type ComponentID uint8 - type EntityVersion uint -type Entity uint32 +type entityType = uint32 +type Entity entityType -func (e Entity) IsVersion(version EntityVersion) bool { +func (e *Entity) IsVersion(version EntityVersion) bool { return e.GetVersion() == version } @@ -25,8 +30,8 @@ func (e *Entity) SetVersion(version EntityVersion) { *e = Entity(entityType(*e) - entityType(e.GetVersion()<<(entityPower-versionPower)) | entityType(version)<<(entityPower-versionPower)) } -func (e Entity) GetVersion() EntityVersion { - return EntityVersion(e >> (entityPower - versionPower)) +func (e *Entity) GetVersion() EntityVersion { + return EntityVersion(*e >> (entityPower - versionPower)) } const ( diff --git a/pkg/ecs/example.go b/pkg/ecs/example.go deleted file mode 100644 index 018c5295..00000000 --- a/pkg/ecs/example.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package ecs - -type Transform struct { - X, Y, Z float32 -} - -type Rotation struct { - RX, RY, RZ int -} - -type BulletSpawn struct{} - -type Bullet struct { - HP int -} - -const ( - invalidID ComponentID = iota - rotationID - transformID - bulletID - bulletSpawnID -) - -var _ = CreateComponentService[Rotation](rotationID) -var transformComponent = CreateComponentService[Transform](transformID) -var bulletSpawnerComponent = CreateComponentService[BulletSpawn](bulletID) -var bulletComponent = CreateComponentService[Bullet](bulletSpawnID) - -type TransformSystem struct { - n int - transform *ComponentManager[Transform] -} - -func (s *TransformSystem) Init(world *EntityManager) { - s.transform = transformComponent.GetManager(world) -} -func (s *TransformSystem) Destroy(world *EntityManager) {} -func (s *TransformSystem) Run(world *EntityManager) { - s.n++ - for _, t := range s.transform.All { - t.X += 1 - t.Y -= 1 - t.Z += 2 - } -} - -type BulletSpawnSystem struct { - n int - bulletSpawner *ComponentManager[BulletSpawn] - transform *ComponentManager[Transform] - bullet *ComponentManager[Bullet] -} - -func (s *BulletSpawnSystem) Init(world *EntityManager) { - s.bulletSpawner = bulletSpawnerComponent.GetManager(world) - s.transform = transformComponent.GetManager(world) - s.bullet = bulletComponent.GetManager(world) -} -func (s *BulletSpawnSystem) Destroy(world *EntityManager) {} -func (s *BulletSpawnSystem) Run(world *EntityManager) { - s.n++ - - var bulletData Bullet - bulletData.HP = 5 - - for id := range s.bulletSpawner.All { - tr := s.transform.Get(id) - if tr == nil { - continue - } - - newBullet := world.Create() - s.transform.Create(newBullet, *tr) - s.bullet.Create(newBullet, bulletData) - } -} - -type BulletSystem struct { - bullet *ComponentManager[Bullet] -} - -func (s *BulletSystem) Init(world *EntityManager) { - s.bullet = bulletComponent.GetManager(world) -} -func (s *BulletSystem) Destroy(world *EntityManager) {} -func (s *BulletSystem) Run(world *EntityManager) { - for entId, b := range s.bullet.All { - b.HP -= 1 - if b.HP <= 0 { - world.Delete(entId) - } - } -} - -type PlayerSpawnSystem struct { - bulletSpawner *ComponentManager[BulletSpawn] - transform *ComponentManager[Transform] -} - -func (s *PlayerSpawnSystem) Init(world *EntityManager) { - s.bulletSpawner = bulletSpawnerComponent.GetManager(world) - s.transform = transformComponent.GetManager(world) - - count := 100_000 - tra := Transform{0, 1, 2} - bs := BulletSpawn{} - - var player Entity - for i := 0; i < count; i++ { - player = world.Create() - s.transform.Create(player, tra) - - if i%2 == 0 { - s.bulletSpawner.Create(player, bs) - } - } -} -func (s *PlayerSpawnSystem) Destroy(world *EntityManager) {} -func (s *PlayerSpawnSystem) Run(world *EntityManager) {} diff --git a/pkg/ecs/example_test.go b/pkg/ecs/example_test.go deleted file mode 100644 index 900b1461..00000000 --- a/pkg/ecs/example_test.go +++ /dev/null @@ -1,172 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package ecs - -// func BenchmarkSystems(b *testing.B) { -// var world = NewEntityManager("Main") - -// world.RegisterComponents( -// &transformComponent, -// &bulletSpawnerComponent, -// &bulletComponent, -// ) - -// world.RegisterSystems(). -// Parallel( -// new(PlayerSpawnSystem), -// new(BulletSpawnSystem), -// ). -// Sequential( -// new(BulletSystem), -// new(TransformSystem), -// ) - -// b.ReportAllocs() -// b.ResetTimer() -// for range b.N { -// world.RunSystems() -// } -// } - -// func BenchmarkEntityUpdate(b *testing.B) { -// b.ReportAllocs() -// count := 1_000_000 - -// var world = New("Main") - -// world.RegisterComponents( -// &bulletSpawnerComponent, -// &transformComponent, -// ) - -// world.RegisterSystems(). -// Parallel( -// new(TransformSystem), -// new(BulletSpawnSystem), -// ). -// Sequential( -// new(BulletSpawnSystem), -// new(TransformSystem), -// ) - -// tra := Transform{0, 1, 2} -// sc := BulletSpawn{} - -// var player *Entity -// transform := transformComponent.Instances(&world) -// bullet := bulletSpawnerComponent.Instances(&world) -// for i := 0; i < count; i++ { -// player = world.CreateEntity("Player") -// if i%2 == 0 { -// transform.Set(player.ID, tra) -// } -// if i%10 == 0 { -// bullet.Set(player.ID, sc) -// } -// } - -// b.ResetTimer() -// for range b.N { -// transform := transformComponent.Instances(&world) - -// for _, v := range transform.All() { -// v.X += 1 -// v.Y -= 1 -// v.Z += 2 -// } -// } -// } - -// func BenchmarkCreateWorld(b *testing.B) { -// b.ReportAllocs() -// count := 1_000_000 - -// b.ResetTimer() -// for range b.N { -// b.StopTimer() -// var world = New("Main") - -// world.RegisterComponents( -// &transformComponent, -// ) - -// tra := Transform{0, 1, 2} - -// var player *Entity -// b.StartTimer() -// transform := transformComponent.Instances(&world) -// for i := 0; i < count; i++ { -// player = world.CreateEntity("Player") -// transform.Set(player.ID, tra) -// } -// } -// } - -// func BenchmarkEntityCreate(b *testing.B) { -// var world = New("Main") -// world.RegisterComponents( -// &bulletSpawnerComponent, -// &transformComponent, -// ) - -// b.ResetTimer() -// b.ReportAllocs() -// var tra Transform - -// transform := transformComponent.Instances(&world) - -// for i := 0; i < b.N; i++ { -// tra.X = float32(i) -// tra.Y = float32(i + 1) -// tra.Z = float32(i + 4) -// player := world.CreateEntity("Player") -// transform.Set(player.ID, tra) -// } -// } - -// func TestEntityUpdate(t *testing.T) { -// var world = New("Main") -// world.RegisterComponents( -// &bulletSpawnerComponent, -// &transformComponent, -// ) - -// transform := transformComponent.Instances(&world) - -// var cases []EntityID -// for i := 0; i < 10_000_000; i++ { -// tra := Transform{float32(i), float32(-i), 2} -// player := world.CreateEntity("Player") -// transform.Set(player.ID, tra) -// cases = append(cases, player.ID) -// } -// // check -// for i, id := range cases { -// tra := Transform{float32(i), float32(-i), 2} - -// if id == 1000000 { -// t.Log("test", id) -// } -// e, ok := world.Entities.Get(id) -// if !ok { -// t.Fatalf("not found entity with id: %v", id) -// } - -// if e.ID != id { -// t.Fatalf("want: %v, got: %v", id, e.ID) -// } - -// entity, ok := transform.Get(e.ID) -// if !ok { -// t.Fatalf("not found component %v", id) -// } - -// if entity != tra { -// t.Fatalf("want: %v, got: %v", tra, entity) -// } -// } -// } diff --git a/pkg/ecs/sparse-set.go b/pkg/ecs/sparse-set.go index 97738034..e2dea609 100644 --- a/pkg/ecs/sparse-set.go +++ b/pkg/ecs/sparse-set.go @@ -6,14 +6,14 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs -type SparseSet[TData any, TKey Entity | ComponentID | int] struct { +type SparseSet[TData any, TKey Entity | ComponentId | int] struct { // TODO: refactor map to a slice with using of a deletedSparseElements slice sparse *ChunkMap[int] denseData *ChunkArray[TData] denseIndex *ChunkArray[TKey] } -func NewSparseSet[TData any, TKey Entity | ComponentID | int]() SparseSet[TData, TKey] { +func NewSparseSet[TData any, TKey Entity | ComponentId | int]() SparseSet[TData, TKey] { set := SparseSet[TData, TKey]{} set.sparse = NewChunkMap[int](5, 10) set.denseData = NewChunkArray[TData](5, 10) diff --git a/pkg/ecs/system.go b/pkg/ecs/system.go index 70e9dc6a..b557eea2 100644 --- a/pkg/ecs/system.go +++ b/pkg/ecs/system.go @@ -12,9 +12,12 @@ type AnySystemPtrDeprecated[W any] interface { Destroy(*W) } -type AnySystemPointer interface { +type AnySystemPtr interface { Init(manager *EntityManager) Update(manager *EntityManager) FixedUpdate(manager *EntityManager) Destroy(manager *EntityManager) } + +type AnySystemListPtr interface{} +type AnySystemList interface{} diff --git a/pkg/ecs/world.go b/pkg/ecs/world.go new file mode 100644 index 00000000..fabcc46a --- /dev/null +++ b/pkg/ecs/world.go @@ -0,0 +1,108 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "reflect" +) + +type World[C, S any] struct { + Entities EntityManager + Components C + Systems S +} + +func NewWorld[C AnyComponentList, S AnySystemList](componentList C, systemList S) World[C, S] { + return World[C, S]{ + Entities: NewEntityManager(), + Components: componentList, + Systems: systemList, + } +} + +func (w *World[C, S]) Init() { + w.injectComponentsToSystems() + w.injectEntityManagerToComponents() + w.Entities.init() +} + +func (w *World[C, S]) Destroy() { + w.Entities.Destroy() + //w.Components.Destroy() + //w.Systems.Destroy() +} + +func (w *World[C, S]) injectEntityManagerToComponents() { + componentList := &w.Components + entityManager := &w.Entities + + reflectedComponentList := reflect.ValueOf(componentList).Elem() + componentListLen := reflectedComponentList.NumField() + + for k := range componentListLen { + component := reflectedComponentList.Field(k) + componentManager, ok := component.Addr().Interface().(AnyComponentManagerPtr) + if !ok { + continue + } + entityManager.registerComponent(componentManager) + componentManager.registerEntityManager(entityManager) + } +} + +// injectToSystems +func (w *World[C, S]) injectComponentsToSystems() { + systemList := &w.Systems + componentList := &w.Components + entityManager := &w.Entities + + reflectedSystemList := reflect.ValueOf(systemList).Elem() + systemsLen := reflectedSystemList.NumField() + + reflectedComponentList := reflect.ValueOf(componentList).Elem() + componentsLen := reflectedComponentList.NumField() + + entityManagerType := reflect.TypeOf(entityManager) + + for i := range systemsLen { + system := reflectedSystemList.Field(i) + systemLen := system.NumField() + + for j := range systemLen { + systemField := system.Field(j) + systemFieldType := systemField.Type() + + if systemFieldType.Kind() != reflect.Ptr { + continue + } + + if systemFieldType == entityManagerType { + system.Field(j).Set(reflect.ValueOf(entityManager)) + continue + } + + // TODO: refactor to component list indexed map to speed up assignment + for k := range componentsLen { + component := reflectedComponentList.Field(k) + componentType := component.Type() + + if systemFieldType.Elem() == componentType { + system.Field(j).Set(component.Addr()) + break + } + } + } + } +} diff --git a/pkg/gomp/basic-components.client.go b/pkg/gomp/basic-components.client.go deleted file mode 100644 index fab4abad..00000000 --- a/pkg/gomp/basic-components.client.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build !server -// +build !server - -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import input "github.com/quasilyte/ebitengine-input" - -var InputComponent = CreateComponent[input.Handler]() diff --git a/pkg/gomp/basic-components.go b/pkg/gomp/basic-components.go deleted file mode 100644 index 6e27f540..00000000 --- a/pkg/gomp/basic-components.go +++ /dev/null @@ -1,16 +0,0 @@ -package gomp - -import ( - "image" - - "github.com/jakecoffman/cp/v2" -) - -var BodyComponent = CreateComponent[cp.Body]() - -type SpriteData struct { - Image image.Image - Config image.Config -} - -var SpriteComponent = CreateComponent[SpriteData]() diff --git a/pkg/gomp/body-system.go b/pkg/gomp/body-system.go deleted file mode 100644 index 2375dcdd..00000000 --- a/pkg/gomp/body-system.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import ( - "log" - "math/rand/v2" - - "github.com/jakecoffman/cp/v2" - "github.com/yohamta/donburi" -) - -var BodySystem = CreateSystem(new(bodySystemController)) - -// physicsSystemController is a system that updates the physics of a game -type bodySystemController struct { - world donburi.World - space *cp.Space -} - -func (c *bodySystemController) Init(game *Game) { - c.space = cp.NewSpace() - c.world = game.World - - BodyComponent.Query.Each(c.world, func(e *donburi.Entry) { - body := BodyComponent.Query.Get(e) - - randX := (rand.Float64()) * 1000 - randY := (rand.Float64()) * 1000 - - body.SetPosition(cp.Vector{X: randX, Y: randY}) - - // randX = (rand.Float64() - 0.5) * 10 - // randY = (rand.Float64() - 0.5) * 10 - - // body.SetVelocity(randX, randY) - - c.space.AddBody(body) - }) -} - -func (c *bodySystemController) Update(dt float64) { - BodyComponent.Query.Each(c.world, func(e *donburi.Entry) { - body := BodyComponent.Query.Get(e) - - if body.IsSleeping() { - log.Println("is sleeping") - return - } - }) - - c.space.Step(dt) -} diff --git a/pkg/gomp/ebiten.go b/pkg/gomp/ebiten.go deleted file mode 100644 index 001917a3..00000000 --- a/pkg/gomp/ebiten.go +++ /dev/null @@ -1,74 +0,0 @@ -//go:build !graphics -// +build !graphics - -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import ( - "log" - "time" - - "github.com/hajimehoshi/ebiten/v2" - input "github.com/quasilyte/ebitengine-input" - "github.com/yohamta/donburi" - "github.com/yohamta/donburi/filter" -) - -type ebitenGame struct { - game *Game - dt time.Duration - lastUpdateAt time.Time - inputSystem *input.System -} - -func (e *ebitenGame) Update() error { - e.dt = time.Since(e.lastUpdateAt) - - e.inputSystem.UpdateWithDelta(e.dt.Seconds()) - e.game.Update(e.dt.Seconds()) - - e.lastUpdateAt = time.Now() - return nil -} - -func (e *ebitenGame) Draw(screen *ebiten.Image) { - // e.game.Draw(screen) - - op := &ebiten.DrawImageOptions{} - - query := donburi.NewQuery(filter.Contains(BodyComponent.Query, RenderComponent.Query)) - - query.Each(e.game.World, func(e *donburi.Entry) { - render := RenderComponent.Query.Get(e) - - if render == nil { - log.Fatalln("RenderComponent is nil") - } - - body := BodyComponent.Query.Get(e) - - if body == nil { - log.Fatalln("BodyComponent is nil") - } - - op.GeoM.Reset() - op.GeoM.Translate(body.Position().X, body.Position().Y) - - screen.DrawImage(render.Sprite, op) - }) -} - -func (e *ebitenGame) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { - return outsideWidth, outsideHeight -} - -func (e *ebitenGame) RegisterInputHandlers(...input.Handler) { - // e.inputSystem -} - -// TODO: create input system that could register the gomp.CreateKeyboardHandler() and gomp.CreateMouseHandler() diff --git a/pkg/gomp/ecs-component.go b/pkg/gomp/ecs-component.go deleted file mode 100644 index b2edd321..00000000 --- a/pkg/gomp/ecs-component.go +++ /dev/null @@ -1,10 +0,0 @@ -package gomp - -import "github.com/yohamta/donburi" - -type IComponent = donburi.IComponentType - -type Component struct { - ComponentType IComponent - Set func(*donburi.Entry) -} diff --git a/pkg/gomp/ecs-entitiy.go b/pkg/gomp/ecs-entitiy.go deleted file mode 100644 index 0fc9965e..00000000 --- a/pkg/gomp/ecs-entitiy.go +++ /dev/null @@ -1,5 +0,0 @@ -package gomp - -import "github.com/yohamta/donburi" - -type Entity func(world donburi.World, extra ...Component) diff --git a/pkg/gomp/ecs-system.go b/pkg/gomp/ecs-system.go deleted file mode 100644 index ef723f63..00000000 --- a/pkg/gomp/ecs-system.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -type System struct { - Controller SystemController - ID uint16 -} - -type SystemController interface { - Init(game *Game) - Update(dt float64) -} - -func (s *System) Init(game *Game) { - s.Controller.Init(game) -} - -func (s *System) Update(dt float64) { - s.Controller.Update(dt) -} diff --git a/pkg/gomp/ecs-world.go b/pkg/gomp/ecs-world.go deleted file mode 100644 index 85806834..00000000 --- a/pkg/gomp/ecs-world.go +++ /dev/null @@ -1,5 +0,0 @@ -package gomp - -import "github.com/yohamta/donburi" - -type World = donburi.World diff --git a/pkg/gomp/game-client.go b/pkg/gomp/game-client.go deleted file mode 100644 index c7daf85c..00000000 --- a/pkg/gomp/game-client.go +++ /dev/null @@ -1,44 +0,0 @@ -//go:build !server -// +build !server - -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import ( - "log" - - "github.com/hajimehoshi/ebiten/v2" - input "github.com/quasilyte/ebitengine-input" -) - -func initInputSystem(cfg input.SystemConfig) input.System { - sys := input.System{} - sys.Init(cfg) - return sys -} - -var InputSystem = initInputSystem(input.SystemConfig{ - DevicesEnabled: input.AnyDevice, -}) - -func (g *Game) Ebiten() *ebitenGame { - g.systems = append(g.systems, EbitenRenderSystem) - EbitenRenderSystem.Init(g) - - e := new(ebitenGame) - e.game = g - e.inputSystem = &InputSystem - - tps := 1 / g.tickRate.Seconds() - ebiten.SetTPS(int(tps)) - if g.Debug { - log.Println("Initial TPS:", tps) - } - - return e -} diff --git a/pkg/gomp/game.go b/pkg/gomp/game.go deleted file mode 100644 index 0e47b83c..00000000 --- a/pkg/gomp/game.go +++ /dev/null @@ -1,169 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import ( - "context" - "fmt" - "log" - "sync" - "time" - - "github.com/yohamta/donburi" -) - -type Game struct { - mx sync.Mutex - wg *sync.WaitGroup - - World donburi.World - systems []System - LoadedScenes map[string]*Scene - Network *Network - - tickRate time.Duration - Debug bool -} - -func (g *Game) Init(tickRate time.Duration) { - g.World = donburi.NewWorld() - // g.systems = []ecs.System{} - g.tickRate = tickRate - g.wg = new(sync.WaitGroup) - g.LoadedScenes = make(map[string]*Scene) - g.Debug = false -} - -func (g *Game) Run(ctx context.Context) { - ticker := time.NewTicker(g.tickRate) - defer ticker.Stop() - - dt := g.tickRate.Seconds() - - g.Update(dt) - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - g.Update(dt) - } - } -} - -func (g *Game) Update(dt float64) { - g.mx.Lock() - defer g.mx.Unlock() - - if g.Debug { - log.Println("=========Game UPDATE START==========") - defer log.Println("=========Game UPDATE FINISH=========") - log.Println("dt:", dt) - } - - dtTreshold := g.tickRate.Seconds() * 2 - - if dt > dtTreshold { - log.Println("WARNING: Game tick rate is too high", dt, dtTreshold) - return - } - - lenSys := len(g.systems) - for i := 0; i < lenSys; i++ { - g.systems[i].Update(dt) - } - - g.wg.Add(len(g.LoadedScenes)) - for i := range g.LoadedScenes { - go updateSystemsAsync(g.LoadedScenes[i], dt, g.wg) - } - g.wg.Wait() -} - -func (g *Game) LoadScene(scene Scene) *Scene { - g.mx.Lock() - defer g.mx.Unlock() - - if g.Debug { - log.Println("Loading scene:", scene.Name) - defer log.Println("Scene loaded:", scene.Name) - } - - //check if scene already exists - suffix := 1 - - for { - prefixedName := fmt.Sprintf("%s_%d", scene.Name, suffix) - - if _, ok := g.LoadedScenes[prefixedName]; ok { - suffix++ - continue - } - - scene.Name = prefixedName - break - } - - c := scene.SceneComponent.New(SceneData{Name: scene.Name}) - entitiesLen := len(scene.Entities) - for i := 0; i < entitiesLen; i++ { - scene.Entities[i](g.World, c) - } - - g.LoadedScenes[scene.Name] = &scene - return &scene -} - -func (g *Game) UnloadScene(scene *Scene) { - g.mx.Lock() - defer g.mx.Unlock() - - if scene == nil { - panic("Trying to unload nil scene") - } - - if g.Debug { - log.Println("Unloading scene: ", scene.Name) - defer log.Println("Scene unloaded: ", scene.Name) - } - - // check if scene exists - if _, ok := g.LoadedScenes[scene.Name]; !ok { - return - } - - scene.SceneComponent.Query.Each(g.World, func(e *donburi.Entry) { - g.World.Remove(e.Entity()) - }) - - delete(g.LoadedScenes, scene.Name) -} - -func (g *Game) UnloadAllScenes() { - for i := range g.LoadedScenes { - g.UnloadScene(g.LoadedScenes[i]) - } -} - -func (g *Game) RegisterSystems(systems ...System) { - g.mx.Lock() - defer g.mx.Unlock() - - for i := range systems { - g.systems = append(g.systems, systems[i]) - g.systems[i].Init(g) - } -} - -func updateSystemsAsync(scene *Scene, dt float64, wg *sync.WaitGroup) { - defer wg.Done() - lenSys := len(scene.Systems) - for i := 0; i < lenSys; i++ { - scene.Systems[i].Update(dt) - } -} diff --git a/pkg/gomp/gomp.go b/pkg/gomp/gomp.go deleted file mode 100644 index b85c68a3..00000000 --- a/pkg/gomp/gomp.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import ( - "fmt" - "reflect" - "time" - - "github.com/yohamta/donburi" -) - -func NewGame(tickRate time.Duration) *Game { - game := new(Game) - - game.Init(tickRate) - - return game -} - -func CreateEntity(components ...Component) func(amount int) []Entity { - return func(amount int) []Entity { - if amount <= 0 { - panic(fmt.Sprint("Adding Entity to scene with (", amount, ") amount failed. Amount must be greater than 0.")) - } - - entArr := make([]Entity, amount) - ent := func(world donburi.World, extra ...Component) { - components := append(components, extra...) - cmpnnts := make([]IComponent, len(components)) - for i, c := range components { - cmpnnts[i] = c.ComponentType - } - - entity := world.Create(cmpnnts...) - entry := world.Entry(entity) - for _, c := range components { - c.Set(entry) - } - } - - for i := 0; i < amount; i++ { - entArr[i] = ent - } - - return entArr - } -} - -type ComponentFactory[T any] struct { - Query *donburi.ComponentType[T] -} - -func CreateComponent[T any]() ComponentFactory[T] { - typeFor := reflect.TypeFor[T]() - - if typeFor.Kind() == reflect.Interface { - panic(fmt.Sprint("CreateComponent[", typeFor.String(), "] failed. Type must not be an interface.")) - } - - return ComponentFactory[T]{Query: donburi.NewComponentType[T]()} -} - -func (cf ComponentFactory[T]) New(data T) Component { - return Component{ - ComponentType: cf.Query, - Set: func(entity *donburi.Entry) { - cf.Query.SetValue(entity, data) - }, - } -} - -// func CreateComponent[T any](initData T) *donburi.ComponentType[T] { -// return donburi.NewComponentType[T](initData) -// } - -var systemId uint16 = 0 - -func CreateSystem(controller SystemController) System { - sys := System{ - ID: systemId, - Controller: controller, - } - - systemId++ - - return sys -} diff --git a/pkg/gomp/network-player.go b/pkg/gomp/network-player.go deleted file mode 100644 index 94ce7d42..00000000 --- a/pkg/gomp/network-player.go +++ /dev/null @@ -1,15 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -type NetworkData struct { - PlayerID int -} - -var NetworkComponent = CreateComponent[NetworkData]() - -var Player = CreateEntity() diff --git a/pkg/gomp/network-system.go b/pkg/gomp/network-system.go deleted file mode 100644 index 4e0eb15c..00000000 --- a/pkg/gomp/network-system.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import ( - "github.com/yohamta/donburi" -) - -var NetworkSystem = CreateSystem(new(networkController)) - -type networkController struct { - world donburi.World - addPlayers chan int - removePlayers chan int - newEvent chan int - sendEvent chan int -} - -func (c *networkController) Init(game *Game) { - c.world = game.World -} - -func (c *networkController) Update(dt float64) { - for i := 0; i < len(c.addPlayers); i++ { - playerId := <-c.addPlayers - - network := NetworkComponent.New(NetworkData{ - PlayerID: playerId, - }) - - player := CreateEntity(network)(1) - playerLen := len(player) - for i := 0; i < playerLen; i++ { - player[i](c.world) - } - - if i >= 9 { - break - } - } - - for i := 0; i < len(c.removePlayers); i++ { - playerId := <-c.removePlayers - - NetworkComponent.Query.Each(c.world, func(e *donburi.Entry) { - network := NetworkComponent.Query.Get(e) - - if network.PlayerID != playerId { - return - } - - ent := e.Entity() - c.world.Remove(ent) - }) - - if i >= 9 { - break - } - } - - for i := 0; i < len(c.newEvent); i++ { - // event := <-c.newEvent - - } - - for i := 0; i < len(c.sendEvent); i++ { - // event := <-c.sendEvent - - } -} diff --git a/pkg/gomp/render-component-ebiten.go b/pkg/gomp/render-component-ebiten.go deleted file mode 100644 index a595660e..00000000 --- a/pkg/gomp/render-component-ebiten.go +++ /dev/null @@ -1,17 +0,0 @@ -package gomp - -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -import ( - "github.com/hajimehoshi/ebiten/v2" -) - -type RenderData struct { - Sprite *ebiten.Image -} - -var RenderComponent = CreateComponent[RenderData]() diff --git a/pkg/gomp/render-system-ebiten.go b/pkg/gomp/render-system-ebiten.go deleted file mode 100644 index a0a7f2cf..00000000 --- a/pkg/gomp/render-system-ebiten.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import ( - "github.com/hajimehoshi/ebiten/v2" - "github.com/yohamta/donburi" -) - -var EbitenRenderSystem = CreateSystem(new(ebitenRenderSystemController)) - -// ebitenRenderSystemController is a system that updates the physics of a game -type ebitenRenderSystemController struct { - world donburi.World -} - -func (c *ebitenRenderSystemController) Init(game *Game) { - c.world = game.World -} - -func (c *ebitenRenderSystemController) Update(dt float64) { - SpriteComponent.Query.Each(c.world, func(e *donburi.Entry) { - sprite := SpriteComponent.Query.Get(e) - if !e.HasComponent(RenderComponent.Query) { - donburi.Add(e, RenderComponent.Query, &RenderData{ - Sprite: ebiten.NewImageFromImage(sprite.Image), - }) - } - }) -} diff --git a/pkg/gomp/resources.go b/pkg/gomp/resources.go deleted file mode 100644 index 4bd87d85..00000000 --- a/pkg/gomp/resources.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -<- Монтажер сука Donated 50 RUB -*/ - -package gomp - -import ( - "embed" - "fmt" - "image/png" - "io/fs" - "log" - "os" -) - -func walkDir(fs fs.ReadDirFS, prefix string, fn func(path string, info os.FileInfo, err error) error) error { - dirEntries, err := fs.ReadDir(prefix) - if err != nil { - return err - } - - for _, entry := range dirEntries { - info, err := entry.Info() - if err != nil { - return err - } - - err = fn(entry.Name(), info, nil) - if err != nil { - return err - } - } - - return nil -} - -func CreateSpriteResource(fs embed.FS, prefix string) func(filename string) SpriteData { - sprites := make(map[string]SpriteData) - - err := walkDir(fs, prefix, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info.IsDir() { - return nil - } - - filename := prefix + "/" + info.Name() - file, err := fs.Open(filename) - if err != nil { - log.Println("Error opening file") - return err - } - defer file.Close() - - img, err := png.Decode(file) - if err != nil { - log.Println("Error decoding file") - return err - } - - fileCfg, err := fs.Open(filename) - if err != nil { - log.Println("Error decoding file") - return err - } - defer fileCfg.Close() - - cfg, err := png.DecodeConfig(fileCfg) - if err != nil { - log.Println("Error decoding cfg file") - return err - } - - sprites[info.Name()] = SpriteData{ - Image: img, - Config: cfg, - } - - return nil - }) - - if err != nil { - panic(err) - } - - return func(filename string) SpriteData { - if sprite, ok := sprites[filename]; ok { - return sprite - } - - panic(fmt.Sprint("File <", filename, "> does not exist in <", prefix, "> resource.")) - } -} diff --git a/pkg/gomp/scene-system.go b/pkg/gomp/scene-system.go deleted file mode 100644 index b4f180e2..00000000 --- a/pkg/gomp/scene-system.go +++ /dev/null @@ -1,16 +0,0 @@ -package gomp - -// func SceneSystem() ecs.System { -// return gomp.CreateSystem(new(sceneSystemController)) -// } - -type sceneSystemController struct { -} - -func (s *sceneSystemController) Init(game *Game) { - -} - -func (s *sceneSystemController) Update(dt float64) { - -} diff --git a/pkg/gomp/scene.go b/pkg/gomp/scene.go deleted file mode 100644 index 67822271..00000000 --- a/pkg/gomp/scene.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -type Scene struct { - Name string - - Systems []*System - Entities []Entity - SceneComponent ComponentFactory[SceneData] -} - -type SceneData struct { - Name string -} - -type sceneFactoryEntities struct { - scene *Scene -} - -func (f sceneFactoryEntities) AddEntities(ent ...[]Entity) Scene { - for i := 0; i <= len(ent); i++ { - f.scene.Entities = ent[0] - } - - return *f.scene -} - -func CreateScene(name string) sceneFactoryEntities { - var SceneComponent = CreateComponent[SceneData]() - scene := new(Scene) - scene.SceneComponent = SceneComponent - - scene.Name = name - - factory := sceneFactoryEntities{ - scene: scene, - } - - return factory -} diff --git a/pkg/gomp/server.go b/pkg/gomp/server.go deleted file mode 100644 index 6c8cb964..00000000 --- a/pkg/gomp/server.go +++ /dev/null @@ -1,203 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package gomp - -import ( - "fmt" - "os" - "runtime" - "strings" - "sync" - "time" - - "net/http" - _ "net/http/pprof" - - "github.com/gorilla/sessions" - "github.com/gorilla/websocket" - "github.com/labstack/echo-contrib/session" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - "github.com/labstack/gommon/log" - echopprof "github.com/sevenNt/echo-pprof" - "golang.org/x/time/rate" -) - -func (game *Game) RunServer() { - runtime.SetCPUProfileRate(1000) - - e := echo.New() - // e.Use(middleware.Logger()) - e.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{ - StackSize: 1 << 10, // 1 KB - LogLevel: log.ERROR, - DisableStackAll: true, - DisablePrintStack: true, - })) - - e.Use(middleware.BodyLimit("2M")) - e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(rate.Limit(60)))) - e.Use(session.Middleware(sessions.NewCookieStore([]byte(getEnv("AUTH_SECRET", "jdkljskldjslk"))))) - e.Use(middleware.GzipWithConfig(middleware.GzipConfig{ - Level: 5, - Skipper: func(c echo.Context) bool { - return strings.Contains(c.Path(), "ws") // Change "metrics" for your own path - }, - })) - - e.GET("/ws", wsHandler(game)) - - echopprof.Wrap(e) - - go e.Start(":27015") -} - -var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { - return true - }, -} - -func wsHandler(game *Game) echo.HandlerFunc { - return func(c echo.Context) error { - if len(game.Network.Host.clients) >= game.Network.Host.MaxClients { - return c.String(http.StatusBadRequest, "Too many players") - } - - conn, err := upgrader.Upgrade(c.Response().Writer, c.Request(), nil) - if err != nil { - return err - } - - return handleWsConnection(c, conn, game) - } -} - -type wsClient struct { - id NetworkPlayerId - conn *websocket.Conn - send chan []byte - receiver chan []byte -} - -func (c *wsClient) Send(msg []byte) { - c.send <- msg -} - -func (c *wsClient) Close() error { - return c.conn.Close() -} - -func handleWsConnection(ctx echo.Context, conn *websocket.Conn, game *Game) error { - var id NetworkPlayerId = 1 - - client := wsClient{} - - client.id = id - client.conn = conn - client.send = make(chan []byte, 512) - - receiver := game.Network.Host.AddClient(id, &client) - defer game.Network.Host.RemoveClient(id) - - client.receiver = receiver - - var wg sync.WaitGroup - wg.Add(2) - - go client.readPump(&wg) - go client.writePump(&wg) - - wg.Wait() - - return conn.Close() -} - -const ( - // Time allowed to write a message to the peer. - writeWait = 10 * time.Second - - // Time allowed to read the next pong message from the peer. - pongWait = 60 * time.Second - - // Send pings to peer with this period. Must be less than pongWait. - pingPeriod = (pongWait * 9) / 10 - - // Maximum message size allowed from peer. - maxMessageSize = 512 -) - -// readPump pumps messages from the websocket connection to the hub. -// -// The application runs readPump in a per-connection goroutine. The application -// ensures that there is at most one reader on a connection by executing all -// reads from this goroutine. -func (c *wsClient) readPump(wg *sync.WaitGroup) { - defer func() { - wg.Done() - }() - - c.conn.SetReadLimit(maxMessageSize) - c.conn.SetReadDeadline(time.Now().Add(pongWait)) - c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) - - for { - _, message, err := c.conn.ReadMessage() - if err != nil { - if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { - fmt.Printf("error: %v", err) - } - return - } - - c.receiver <- message - } -} - -// writePump pumps messages from the hub to the websocket connection. -// -// A goroutine running writePump is started for each connection. The -// application ensures that there is at most one writer to a connection by -// executing all writes from this goroutine. -func (c *wsClient) writePump(wg *sync.WaitGroup) { - defer wg.Done() - - pingTicker := time.NewTicker(pingPeriod) - defer pingTicker.Stop() - - for { - select { - case message, ok := <-c.send: - c.conn.SetWriteDeadline(time.Now().Add(writeWait)) - if !ok { - // The hub closed the channel. - c.conn.WriteMessage(websocket.CloseMessage, []byte{}) - return - } - - err := c.conn.WriteMessage(websocket.BinaryMessage, message) - if err != nil { - return - } - - case <-pingTicker.C: - c.conn.SetWriteDeadline(time.Now().Add(writeWait)) - if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil { - return - } - } - } -} - -func getEnv(key, fallback string) string { - if value, ok := os.LookupEnv(key); ok { - return value - } - return fallback -} diff --git a/pkg/legacy/network-system.go b/pkg/legacy/network-system.go new file mode 100644 index 00000000..a4b3fe54 --- /dev/null +++ b/pkg/legacy/network-system.go @@ -0,0 +1,74 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package legacy + +//import ( +// "github.com/yohamta/donburi" +//) +// +//var NetworkSystem = CreateSystem(new(networkController)) +// +//type networkController struct { +// world donburi.World +// addPlayers chan int +// removePlayers chan int +// newEvent chan int +// sendEvent chan int +//} +// +//func (c *networkController) Init(game *Game) { +// c.world = game.World +//} +// +//func (c *networkController) Update(dt float64) { +// for i := 0; i < len(c.addPlayers); i++ { +// playerId := <-c.addPlayers +// +// network := NetworkComponent.New(NetworkData{ +// PlayerID: playerId, +// }) +// +// player := CreateEntity(network)(1) +// playerLen := len(player) +// for i := 0; i < playerLen; i++ { +// player[i](c.world) +// } +// +// if i >= 9 { +// break +// } +// } +// +// for i := 0; i < len(c.removePlayers); i++ { +// playerId := <-c.removePlayers +// +// NetworkComponent.Query.Each(c.world, func(e *donburi.Entry) { +// network := NetworkComponent.Query.Get(e) +// +// if network.PlayerID != playerId { +// return +// } +// +// ent := e.Entity() +// c.world.Remove(ent) +// }) +// +// if i >= 9 { +// break +// } +// } +// +// for i := 0; i < len(c.newEvent); i++ { +// // event := <-c.newEvent +// +// } +// +// for i := 0; i < len(c.sendEvent); i++ { +// // event := <-c.sendEvent +// +// } +//} diff --git a/pkg/gomp/network.go b/pkg/legacy/network.go similarity index 99% rename from pkg/gomp/network.go rename to pkg/legacy/network.go index ee7c16db..5f994f86 100644 --- a/pkg/gomp/network.go +++ b/pkg/legacy/network.go @@ -4,7 +4,7 @@ Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package gomp +package legacy type NetworkMode int diff --git a/pkg/legacy/resources.go b/pkg/legacy/resources.go new file mode 100644 index 00000000..157853f0 --- /dev/null +++ b/pkg/legacy/resources.go @@ -0,0 +1,99 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +<- Монтажер сука Donated 50 RUB +*/ + +package legacy + +//import ( +// "embed" +// "fmt" +// "image/png" +// "io/fs" +// "log" +// "os" +//) +// +//func walkDir(fs fs.ReadDirFS, prefix string, fn func(path string, info os.FileInfo, err error) error) error { +// dirEntries, err := fs.ReadDir(prefix) +// if err != nil { +// return err +// } +// +// for _, entry := range dirEntries { +// info, err := entry.Info() +// if err != nil { +// return err +// } +// +// err = fn(entry.Name(), info, nil) +// if err != nil { +// return err +// } +// } +// +// return nil +//} +// +//func CreateSpriteResource(fs embed.FS, prefix string) func(filename string) SpriteData { +// sprites := make(map[string]SpriteData) +// +// err := walkDir(fs, prefix, func(path string, info os.FileInfo, err error) error { +// if err != nil { +// return err +// } +// +// if info.IsDir() { +// return nil +// } +// +// filename := prefix + "/" + info.Name() +// file, err := fs.Open(filename) +// if err != nil { +// log.Println("Error opening file") +// return err +// } +// defer file.Close() +// +// img, err := png.Decode(file) +// if err != nil { +// log.Println("Error decoding file") +// return err +// } +// +// fileCfg, err := fs.Open(filename) +// if err != nil { +// log.Println("Error decoding file") +// return err +// } +// defer fileCfg.Close() +// +// cfg, err := png.DecodeConfig(fileCfg) +// if err != nil { +// log.Println("Error decoding cfg file") +// return err +// } +// +// sprites[info.Name()] = SpriteData{ +// Image: img, +// Config: cfg, +// } +// +// return nil +// }) +// +// if err != nil { +// panic(err) +// } +// +// return func(filename string) SpriteData { +// if sprite, ok := sprites[filename]; ok { +// return sprite +// } +// +// panic(fmt.Sprint("File <", filename, "> does not exist in <", prefix, "> resource.")) +// } +//} diff --git a/pkg/legacy/server.go b/pkg/legacy/server.go new file mode 100644 index 00000000..45dbce4d --- /dev/null +++ b/pkg/legacy/server.go @@ -0,0 +1,203 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package legacy + +//import ( +// "fmt" +// "os" +// "runtime" +// "strings" +// "sync" +// "time" +// +// "net/http" +// _ "net/http/pprof" +// +// "github.com/gorilla/sessions" +// "github.com/gorilla/websocket" +// "github.com/labstack/echo-contrib/session" +// "github.com/labstack/echo/v4" +// "github.com/labstack/echo/v4/middleware" +// "github.com/labstack/gommon/log" +// echopprof "github.com/sevenNt/echo-pprof" +// "golang.org/x/time/rate" +//) +// +//func (game *Game) RunServer() { +// runtime.SetCPUProfileRate(1000) +// +// e := echo.New() +// // e.Use(middleware.Logger()) +// e.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{ +// StackSize: 1 << 10, // 1 KB +// LogLevel: log.ERROR, +// DisableStackAll: true, +// DisablePrintStack: true, +// })) +// +// e.Use(middleware.BodyLimit("2M")) +// e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(rate.Limit(60)))) +// e.Use(session.Middleware(sessions.NewCookieStore([]byte(getEnv("AUTH_SECRET", "jdkljskldjslk"))))) +// e.Use(middleware.GzipWithConfig(middleware.GzipConfig{ +// Level: 5, +// Skipper: func(c echo.Context) bool { +// return strings.Contains(c.Path(), "ws") // Change "metrics" for your own path +// }, +// })) +// +// e.GET("/ws", wsHandler(game)) +// +// echopprof.Wrap(e) +// +// go e.Start(":27015") +//} +// +//var upgrader = websocket.Upgrader{ +// ReadBufferSize: 1024, +// WriteBufferSize: 1024, +// CheckOrigin: func(r *http.Request) bool { +// return true +// }, +//} +// +//func wsHandler(game *Game) echo.HandlerFunc { +// return func(c echo.Context) error { +// if len(game.Network.Host.clients) >= game.Network.Host.MaxClients { +// return c.String(http.StatusBadRequest, "Too many players") +// } +// +// conn, err := upgrader.Upgrade(c.Response().Writer, c.Request(), nil) +// if err != nil { +// return err +// } +// +// return handleWsConnection(c, conn, game) +// } +//} +// +//type wsClient struct { +// id NetworkPlayerId +// conn *websocket.Conn +// send chan []byte +// receiver chan []byte +//} +// +//func (c *wsClient) Send(msg []byte) { +// c.send <- msg +//} +// +//func (c *wsClient) Close() error { +// return c.conn.Close() +//} +// +//func handleWsConnection(ctx echo.Context, conn *websocket.Conn, game *Game) error { +// var id NetworkPlayerId = 1 +// +// client := wsClient{} +// +// client.id = id +// client.conn = conn +// client.send = make(chan []byte, 512) +// +// receiver := game.Network.Host.AddClient(id, &client) +// defer game.Network.Host.RemoveClient(id) +// +// client.receiver = receiver +// +// var wg sync.WaitGroup +// wg.Add(2) +// +// go client.readPump(&wg) +// go client.writePump(&wg) +// +// wg.Wait() +// +// return conn.Close() +//} +// +//const ( +// // Time allowed to write a message to the peer. +// writeWait = 10 * time.Second +// +// // Time allowed to read the next pong message from the peer. +// pongWait = 60 * time.Second +// +// // Send pings to peer with this period. Must be less than pongWait. +// pingPeriod = (pongWait * 9) / 10 +// +// // Maximum message size allowed from peer. +// maxMessageSize = 512 +//) +// +//// readPump pumps messages from the websocket connection to the hub. +//// +//// The application runs readPump in a per-connection goroutine. The application +//// ensures that there is at most one reader on a connection by executing all +//// reads from this goroutine. +//func (c *wsClient) readPump(wg *sync.WaitGroup) { +// defer func() { +// wg.Done() +// }() +// +// c.conn.SetReadLimit(maxMessageSize) +// c.conn.SetReadDeadline(time.Now().Add(pongWait)) +// c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) +// +// for { +// _, message, err := c.conn.ReadMessage() +// if err != nil { +// if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { +// fmt.Printf("error: %v", err) +// } +// return +// } +// +// c.receiver <- message +// } +//} +// +//// writePump pumps messages from the hub to the websocket connection. +//// +//// A goroutine running writePump is started for each connection. The +//// application ensures that there is at most one writer to a connection by +//// executing all writes from this goroutine. +//func (c *wsClient) writePump(wg *sync.WaitGroup) { +// defer wg.Done() +// +// pingTicker := time.NewTicker(pingPeriod) +// defer pingTicker.Stop() +// +// for { +// select { +// case message, ok := <-c.send: +// c.conn.SetWriteDeadline(time.Now().Add(writeWait)) +// if !ok { +// // The hub closed the channel. +// c.conn.WriteMessage(websocket.CloseMessage, []byte{}) +// return +// } +// +// err := c.conn.WriteMessage(websocket.BinaryMessage, message) +// if err != nil { +// return +// } +// +// case <-pingTicker.C: +// c.conn.SetWriteDeadline(time.Now().Add(writeWait)) +// if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil { +// return +// } +// } +// } +//} +// +//func getEnv(key, fallback string) string { +// if value, ok := os.LookupEnv(key); ok { +// return value +// } +// return fallback +//} diff --git a/stdcomponents/animation-player.go b/stdcomponents/animation-player.go index b4529ad9..bb2ca5b8 100644 --- a/stdcomponents/animation-player.go +++ b/stdcomponents/animation-player.go @@ -34,6 +34,6 @@ type AnimationPlayer struct { type AnimationPlayerComponentManager = ecs.ComponentManager[AnimationPlayer] -func NewAnimationPlayerComponentManager(world *ecs.EntityManager) AnimationPlayerComponentManager { - return ecs.NewComponentManager[AnimationPlayer](world, ANIMATION_PLAYER_ID) +func NewAnimationPlayerComponentManager() AnimationPlayerComponentManager { + return ecs.NewComponentManager[AnimationPlayer](ANIMATION_PLAYER_ID) } diff --git a/stdcomponents/animation-state.go b/stdcomponents/animation-state.go index 32296703..4c3b20c3 100644 --- a/stdcomponents/animation-state.go +++ b/stdcomponents/animation-state.go @@ -22,6 +22,6 @@ type AnimationState int32 type AnimationStateComponentManager = ecs.ComponentManager[AnimationState] -func NewAnimationStateComponentManager(world *ecs.EntityManager) AnimationStateComponentManager { - return ecs.NewComponentManager[AnimationState](world, ANIMATION_STATE_ID) +func NewAnimationStateComponentManager() AnimationStateComponentManager { + return ecs.NewComponentManager[AnimationState](ANIMATION_STATE_ID) } diff --git a/stdcomponents/flip.go b/stdcomponents/flip.go index 12afbe00..ae6d797f 100644 --- a/stdcomponents/flip.go +++ b/stdcomponents/flip.go @@ -22,6 +22,6 @@ type Flip struct { type FlipComponentManager = ecs.ComponentManager[Flip] -func NewFlipComponentManager(world *ecs.EntityManager) FlipComponentManager { - return ecs.NewComponentManager[Flip](world, FLIP_ID) +func NewFlipComponentManager() FlipComponentManager { + return ecs.NewComponentManager[Flip](FLIP_ID) } diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index c4c4595f..e005bc59 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -11,7 +11,7 @@ import ( ) const ( - INVALID_ID ecs.ComponentID = iota + 128 + INVALID_ID ecs.ComponentId = iota + 128 POSITION_ID ROTATION_ID SCALE_ID diff --git a/stdcomponents/network.go b/stdcomponents/network.go index aa30a39e..98b8d14f 100644 --- a/stdcomponents/network.go +++ b/stdcomponents/network.go @@ -25,6 +25,6 @@ type Network struct { type NetworkComponentManager = ecs.ComponentManager[Network] -func NewNetworkComponentManager(world *ecs.EntityManager) NetworkComponentManager { - return ecs.NewComponentManager[Network](world, NETWORK_ID) +func NewNetworkComponentManager() NetworkComponentManager { + return ecs.NewComponentManager[Network](NETWORK_ID) } diff --git a/stdcomponents/position.go b/stdcomponents/position.go index ea65c26b..655d7fa2 100644 --- a/stdcomponents/position.go +++ b/stdcomponents/position.go @@ -22,6 +22,6 @@ type Position struct { type PositionComponentManager = ecs.ComponentManager[Position] -func NewPositionComponentManager(world *ecs.EntityManager) PositionComponentManager { - return ecs.NewComponentManager[Position](world, POSITION_ID) +func NewPositionComponentManager() PositionComponentManager { + return ecs.NewComponentManager[Position](POSITION_ID) } diff --git a/stdcomponents/rotation.go b/stdcomponents/rotation.go index e7997dd0..ad6eaaa3 100644 --- a/stdcomponents/rotation.go +++ b/stdcomponents/rotation.go @@ -22,6 +22,6 @@ type Rotation struct { type RotationComponentManager = ecs.ComponentManager[Rotation] -func NewRotationComponentManager(world *ecs.EntityManager) RotationComponentManager { - return ecs.NewComponentManager[Rotation](world, ROTATION_ID) +func NewRotationComponentManager() RotationComponentManager { + return ecs.NewComponentManager[Rotation](ROTATION_ID) } diff --git a/stdcomponents/scale.go b/stdcomponents/scale.go index c6450e70..6f1dd41d 100644 --- a/stdcomponents/scale.go +++ b/stdcomponents/scale.go @@ -22,6 +22,6 @@ type Scale struct { type ScaleComponentManager = ecs.ComponentManager[Scale] -func NewScaleComponentManager(world *ecs.EntityManager) ScaleComponentManager { - return ecs.NewComponentManager[Scale](world, SCALE_ID) +func NewScaleComponentManager() ScaleComponentManager { + return ecs.NewComponentManager[Scale](SCALE_ID) } diff --git a/stdcomponents/sprite-matrix.go b/stdcomponents/sprite-matrix.go index 2d8d9d03..ea1cc2ed 100644 --- a/stdcomponents/sprite-matrix.go +++ b/stdcomponents/sprite-matrix.go @@ -36,6 +36,6 @@ type SpriteMatrix struct { type SpriteMatrixComponentManager = ecs.ComponentManager[SpriteMatrix] -func NewSpriteMatrixComponentManager(world *ecs.EntityManager) SpriteMatrixComponentManager { - return ecs.NewComponentManager[SpriteMatrix](world, SPRITE_MATRIX_ID) +func NewSpriteMatrixComponentManager() SpriteMatrixComponentManager { + return ecs.NewComponentManager[SpriteMatrix](SPRITE_MATRIX_ID) } diff --git a/stdcomponents/sprite-sheet.go b/stdcomponents/sprite-sheet.go index 9c53a17a..33ca57d1 100644 --- a/stdcomponents/sprite-sheet.go +++ b/stdcomponents/sprite-sheet.go @@ -30,6 +30,6 @@ type SpriteSheet struct { type SpriteSheetComponentManager = ecs.ComponentManager[SpriteSheet] -func NewSpriteSheetComponentManager(world *ecs.EntityManager) SpriteSheetComponentManager { - return ecs.NewComponentManager[SpriteSheet](world, SPRITE_SHEET_ID) +func NewSpriteSheetComponentManager() SpriteSheetComponentManager { + return ecs.NewComponentManager[SpriteSheet](SPRITE_SHEET_ID) } diff --git a/stdcomponents/sprite.go b/stdcomponents/sprite.go index e378d96c..8656ef8e 100644 --- a/stdcomponents/sprite.go +++ b/stdcomponents/sprite.go @@ -29,6 +29,6 @@ type Sprite struct { type SpriteComponentManager = ecs.ComponentManager[Sprite] -func NewSpriteComponentManager(world *ecs.EntityManager) SpriteComponentManager { - return ecs.NewComponentManager[Sprite](world, SPRITE_ID) +func NewSpriteComponentManager() SpriteComponentManager { + return ecs.NewComponentManager[Sprite](SPRITE_ID) } diff --git a/stdcomponents/texture-render.go b/stdcomponents/texture-render.go index 5c2fcf6a..96cbf457 100644 --- a/stdcomponents/texture-render.go +++ b/stdcomponents/texture-render.go @@ -31,6 +31,6 @@ type TextureRender struct { type TextureRenderComponentManager = ecs.ComponentManager[TextureRender] -func NewTextureRenderComponentManager(world *ecs.EntityManager) TextureRenderComponentManager { - return ecs.NewComponentManager[TextureRender](world, TEXTURE_RENDER_ID) +func NewTextureRenderComponentManager() TextureRenderComponentManager { + return ecs.NewComponentManager[TextureRender](TEXTURE_RENDER_ID) } diff --git a/stdcomponents/tint.go b/stdcomponents/tint.go index 75bd45c5..d405a100 100644 --- a/stdcomponents/tint.go +++ b/stdcomponents/tint.go @@ -23,6 +23,6 @@ type Tint = color.RGBA type TintComponentManager = ecs.ComponentManager[Tint] -func NewTintComponentManager(world *ecs.EntityManager) TintComponentManager { - return ecs.NewComponentManager[Tint](world, TINT_ID) +func NewTintComponentManager() TintComponentManager { + return ecs.NewComponentManager[Tint](TINT_ID) } diff --git a/stdcomponents/velocity.go b/stdcomponents/velocity.go index f4a24bce..baafc378 100644 --- a/stdcomponents/velocity.go +++ b/stdcomponents/velocity.go @@ -22,6 +22,6 @@ type Velocity struct { type VelocityComponentManager = ecs.ComponentManager[Velocity] -func NewVelocityComponentManager(world *ecs.EntityManager) VelocityComponentManager { - return ecs.NewComponentManager[Velocity](world, VELOCITY_ID) +func NewVelocityComponentManager() VelocityComponentManager { + return ecs.NewComponentManager[Velocity](VELOCITY_ID) } diff --git a/stdsystems/debug.go b/stdsystems/debug.go index c3e09d4a..b4967c27 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -35,7 +35,10 @@ func (s *DebugSystem) Run(dt time.Duration) { if err != nil { log.Fatal(err) } - pprof.StartCPUProfile(f) + err = pprof.StartCPUProfile(f) + if err != nil { + log.Fatal(err) + } fmt.Println("CPU Profile Started") } diff --git a/stdsystems/render.go b/stdsystems/render.go index 6f526c71..6be87b5b 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -19,8 +19,9 @@ func NewRenderSystem() RenderSystem { } type RenderSystem struct { - World *ecs.EntityManager - TextureRenders *ecs.ComponentManager[stdcomponents.TextureRender] + EntityManager *ecs.EntityManager + TextureRenders *stdcomponents.TextureRenderComponentManager + Positions *stdcomponents.PositionComponentManager } func (s *RenderSystem) Init() { @@ -32,7 +33,6 @@ func (s *RenderSystem) Run(dt time.Duration) bool { if rl.WindowShouldClose() { return false } - rl.BeginDrawing() rl.ClearBackground(rl.Black) @@ -45,7 +45,13 @@ func (s *RenderSystem) Run(dt time.Duration) bool { // rl.DrawRectangle(0, 0, 120, 120, rl.DarkGray) rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d", s.World.Size()), 10, 30, 20, rl.Red) + rl.DrawText(fmt.Sprintf("%d", s.EntityManager.Size()), 10, 30, 20, rl.Red) + + s.Positions.AllData(func(p *stdcomponents.Position) bool { + rl.DrawCircle(int32(p.X), int32(p.Y), 10, rl.Red) + return true + }) + rl.DrawText(fmt.Sprintf("%s", dt), 10, 50, 20, rl.Red) rl.EndDrawing() From ef9f8e0ab35694caefb7c1d0da7bd41feddbb866 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 21 Feb 2025 21:02:47 +0300 Subject: [PATCH 015/196] small changes --- examples/new-api/components/controller.go | 25 +++ examples/new-api/components/hp.go | 2 +- examples/new-api/components/ids.go | 5 +- examples/new-api/instances/component-list.go | 8 +- examples/new-api/systems/player.go | 63 ++++--- pkg/ecs/bit-array.go | 88 --------- pkg/ecs/{bit-set.go => component-bitset.go} | 0 pkg/ecs/component-manager.go | 2 +- pkg/ecs/component-manager_test.go | 187 ------------------- stdcomponents/animation-player.go | 2 +- stdcomponents/animation-state.go | 2 +- stdcomponents/flip.go | 2 +- stdcomponents/ids.go | 28 +-- stdcomponents/network.go | 2 +- stdcomponents/position.go | 2 +- stdcomponents/rotation.go | 2 +- stdcomponents/scale.go | 2 +- stdcomponents/sprite-matrix.go | 2 +- stdcomponents/sprite-sheet.go | 2 +- stdcomponents/sprite.go | 2 +- stdcomponents/texture-render.go | 2 +- stdcomponents/tint.go | 2 +- stdcomponents/velocity.go | 2 +- 23 files changed, 97 insertions(+), 337 deletions(-) create mode 100644 examples/new-api/components/controller.go delete mode 100644 pkg/ecs/bit-array.go rename pkg/ecs/{bit-set.go => component-bitset.go} (100%) delete mode 100644 pkg/ecs/component-manager_test.go diff --git a/examples/new-api/components/controller.go b/examples/new-api/components/controller.go new file mode 100644 index 00000000..ec245041 --- /dev/null +++ b/examples/new-api/components/controller.go @@ -0,0 +1,25 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +type Controller struct{} + +type ControllerComponentManager = ecs.ComponentManager[Controller] + +func NewControllerComponentManager() ControllerComponentManager { + return ecs.NewComponentManager[Controller](ControllerComponentId) +} diff --git a/examples/new-api/components/hp.go b/examples/new-api/components/hp.go index 0f241d4a..49e63387 100644 --- a/examples/new-api/components/hp.go +++ b/examples/new-api/components/hp.go @@ -23,5 +23,5 @@ type Health struct { type HealthComponentManager = ecs.ComponentManager[Health] func NewHealthComponentManager() HealthComponentManager { - return ecs.NewComponentManager[Health](HEALTH_ID) + return ecs.NewComponentManager[Health](HealthComponentId) } diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go index 23ce985c..0fc99884 100644 --- a/examples/new-api/components/ids.go +++ b/examples/new-api/components/ids.go @@ -17,6 +17,7 @@ package components import "gomp/pkg/ecs" const ( - INVALID_ID ecs.ComponentId = iota - HEALTH_ID + InvalidComponentId ecs.ComponentId = iota + HealthComponentId + ControllerComponentId ) diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index 773d0756..b0c734c9 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -33,7 +33,9 @@ type ComponentList struct { AnimationState stdcomponents.AnimationStateComponentManager TextureRender stdcomponents.TextureRenderComponentManager Network stdcomponents.NetworkComponentManager - Health components.HealthComponentManager + + Health components.HealthComponentManager + Controller components.ControllerComponentManager } func NewComponentList() ComponentList { @@ -51,6 +53,8 @@ func NewComponentList() ComponentList { AnimationState: stdcomponents.NewAnimationStateComponentManager(), TextureRender: stdcomponents.NewTextureRenderComponentManager(), Network: stdcomponents.NewNetworkComponentManager(), - Health: components.NewHealthComponentManager(), + + Health: components.NewHealthComponentManager(), + Controller: components.NewControllerComponentManager(), } } diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 35d12b15..28fcb056 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -8,6 +8,7 @@ package systems import ( rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/components" "gomp/examples/new-api/entities" "gomp/pkg/ecs" "gomp/stdcomponents" @@ -30,7 +31,8 @@ type PlayerSystem struct { AnimationStates *stdcomponents.AnimationStateComponentManager Tints *stdcomponents.TintComponentManager Flips *stdcomponents.FlipComponentManager - isDeleted bool + HP *components.HealthComponentManager + Controllers *components.ControllerComponentManager } func (s *PlayerSystem) Init() { @@ -38,6 +40,9 @@ func (s *PlayerSystem) Init() { s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, ) + + s.Controllers.Create(s.Player.Entity, components.Controller{}) + s.Player.Position.X = 100 s.Player.Position.Y = 100 } @@ -49,36 +54,36 @@ func (s *PlayerSystem) Run(dt time.Duration) { s.Player.Velocity.X = 0 s.Player.Velocity.Y = 0 - if s.isDeleted { - return - } - if rl.IsKeyDown(rl.KeySpace) { - *animationState = entities.PlayerStateJump - } else { - *animationState = entities.PlayerStateIdle - if rl.IsKeyDown(rl.KeyD) { - *animationState = entities.PlayerStateWalk - s.Player.Velocity.X = speed - s.Player.Flip.X = false - } - if rl.IsKeyDown(rl.KeyA) { - *animationState = entities.PlayerStateWalk - s.Player.Velocity.X = -speed - s.Player.Flip.X = true + s.Controllers.AllData(func(c *components.Controller) bool { + if rl.IsKeyDown(rl.KeySpace) { + *animationState = entities.PlayerStateJump + } else { + *animationState = entities.PlayerStateIdle + if rl.IsKeyDown(rl.KeyD) { + *animationState = entities.PlayerStateWalk + s.Player.Velocity.X = speed + s.Player.Flip.X = false + } + if rl.IsKeyDown(rl.KeyA) { + *animationState = entities.PlayerStateWalk + s.Player.Velocity.X = -speed + s.Player.Flip.X = true + } + if rl.IsKeyDown(rl.KeyW) { + *animationState = entities.PlayerStateWalk + s.Player.Velocity.Y = -speed + } + if rl.IsKeyDown(rl.KeyS) { + *animationState = entities.PlayerStateWalk + s.Player.Velocity.Y = speed + } } - if rl.IsKeyDown(rl.KeyW) { - *animationState = entities.PlayerStateWalk - s.Player.Velocity.Y = -speed - } - if rl.IsKeyDown(rl.KeyS) { - *animationState = entities.PlayerStateWalk - s.Player.Velocity.Y = speed + + if rl.IsKeyPressed(rl.KeyK) { + s.EntityManager.Delete(s.Player.Entity) } - } + return true + }) - if rl.IsKeyPressed(rl.KeyK) { - s.EntityManager.Delete(s.Player.Entity) - s.isDeleted = true - } } func (s *PlayerSystem) Destroy() {} diff --git a/pkg/ecs/bit-array.go b/pkg/ecs/bit-array.go deleted file mode 100644 index 9d2e56fe..00000000 --- a/pkg/ecs/bit-array.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package ecs - -import ( - "math/bits" -) - -// type BitArray []uint64 - -// // New creates a new BitArray with the given number of bits. -// func NewBitArray(size uint64) BitArray { -// words := (size + 63) / 64 -// return make(BitArray, words) -// } - -// // ensureIndex ensures the given bit index is accessible, resizing the array if necessary. -// func (b *BitArray) ensureIndex(index uint64) { -// wordsNeeded := (index / 64) + 1 -// if uint64(len(*b)) < wordsNeeded { -// newData := make(BitArray, wordsNeeded) -// copy(newData, *b) -// *b = newData -// } -// } - -// // Resize adjusts the BitArray size to accommodate a specific number of bits. -// // Shrinks the underlying array if the new size is smaller. -// func (b *BitArray) Resize(newSize uint64) { -// newWords := (newSize + 63) / 64 -// currentWords := uint64(len(*b)) -// if newWords > currentWords { -// newData := make(BitArray, newWords) -// copy(newData, *b) -// *b = newData -// } else if newWords < currentWords { -// *b = (*b)[:newWords] -// } -// } -// // Size calculates the total number of bits in the BitArray. -// func (b BitArray) Size() uint64 { -// return uint64(len(b)) * 64 -// } - -// ComponentBitArray256 is a structure to manage an array of uint64 values as a bit array. -const bit_array_size = 256 / bits.UintSize - -type ComponentBitArray256 [bit_array_size]uint - -// Set sets the bit at the given index to 1. -func (b *ComponentBitArray256) Set(index ComponentId) { - b[index/bits.UintSize] |= 1 << (index % bits.UintSize) -} - -// Unset clears the bit at the given index (sets it to 0). -func (b *ComponentBitArray256) Unset(index ComponentId) { - b[index/bits.UintSize] &^= 1 << (index % bits.UintSize) -} - -// Toggle toggles the bit at the given index. -func (b *ComponentBitArray256) Toggle(index ComponentId) { - b[index/bits.UintSize] ^= 1 << (index % bits.UintSize) -} - -// IsSet checks if the bit at the given index is set (1). Automatically resizes if the index is out of bounds. -func (b *ComponentBitArray256) IsSet(index ComponentId) bool { - return (b[index/bits.UintSize] & (1 << (index % bits.UintSize))) != 0 -} - -func (b *ComponentBitArray256) AllSet(yield func(ComponentId) bool) { - var id ComponentId - var raisedBitsCount int - for i, v := range b { - raisedBitsCount = bits.OnesCount(v) - for range raisedBitsCount { - index := bits.Len(v) - 1 - v &^= 1 << index - id = ComponentId(i*bits.UintSize + index) - if !yield(id) { - return - } - } - } -} diff --git a/pkg/ecs/bit-set.go b/pkg/ecs/component-bitset.go similarity index 100% rename from pkg/ecs/bit-set.go rename to pkg/ecs/component-bitset.go diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 7c1fd735..aaac3660 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -16,7 +16,7 @@ import ( // Contracts // ================ -type ComponentId uint +type ComponentId uint16 type AnyComponentList interface{} type AnyComponentListPtr interface{} diff --git a/pkg/ecs/component-manager_test.go b/pkg/ecs/component-manager_test.go deleted file mode 100644 index 54e74ef0..00000000 --- a/pkg/ecs/component-manager_test.go +++ /dev/null @@ -1,187 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package ecs - -import ( - "image/color" - "math/rand" - "testing" -) - -type pixel struct { - x int32 - y int32 - hp int32 - color color.RGBA - breath bool -} - -var pixelComponentType = CreateComponentService[pixel](1) - -// Commonly used functions in both benchmarks. -func PrepareWorld(description string, system AnySystemServicePtr) *EntityManager { - world := NewEntityManager(description) - - world.RegisterComponentServices( - &pixelComponentType, - ) - world.RegisterSystems(). - Parallel( - system, - ) - - return world -} - -func InitPixelComponent(pixelComponent *ComponentManager[pixel], world *EntityManager) { - pixelComponent = pixelComponentType.GetManager(world) - determRand := rand.New(rand.NewSource(42)) - - for i := range 1000 { - for j := range 1000 { - newPixel := world.Create("Pixel") - - randomGreen := uint8(135 / (determRand.Intn(10) + 1)) - randomBlue := uint8(135 / (determRand.Intn(10) + 1)) - - randomColor := color.RGBA{ - G: randomGreen, - B: randomBlue, - A: 255, - } - pixelComponent.Create(newPixel, pixel{ - x: int32(j), - y: int32(i), - hp: 100, - color: randomColor, - }) - } - } -} - -type pixelSystem struct { - pixelComponent ComponentManager[pixel] -} - -func (s *pixelSystem) Init(world *EntityManager) { - InitPixelComponent(&s.pixelComponent, world) -} - -func (s *pixelSystem) Destroy(world *EntityManager) {} - -func (s *pixelSystem) Update(world *EntityManager) {} -func (s *pixelSystem) FixedUpdate(world *EntityManager) { - for pixel := range s.pixelComponent.AllData { - // Note: was not extracted to separate function to simulate - // real-world interaction between range loop and inner code. - color := &pixel.color - - if pixel.breath { - if color.G < 135 { - color.G++ - } else { - pixel.hp++ - } - if color.B < 135 { - color.B++ - } else { - pixel.hp++ - } - } else { - if color.G > 0 { - color.G-- - } else { - pixel.hp-- - } - if color.B > 0 { - color.B-- - } else { - pixel.hp-- - } - } - - if pixel.hp <= 0 { - pixel.breath = true - } else if pixel.hp >= 100 { - pixel.breath = false - } - } -} - -// Direct call iteration type -type pixelSystemDirectCall struct { - pixelComponent ComponentManager[pixel] -} - -func (s *pixelSystemDirectCall) Init(world *EntityManager) { - InitPixelComponent(&s.pixelComponent, world) -} - -func (s *pixelSystemDirectCall) Destroy(world *EntityManager) {} -func (s *pixelSystemDirectCall) Update(world *EntityManager) {} -func (s *pixelSystemDirectCall) FixedUpdate(world *EntityManager) { - s.pixelComponent.AllDataParallel(func(pixel *pixel) bool { - color := &pixel.color - - if pixel.breath { - if color.G < 135 { - color.G++ - } else { - pixel.hp++ - } - if color.B < 135 { - color.B++ - } else { - pixel.hp++ - } - } else { - if color.G > 0 { - color.G-- - } else { - pixel.hp-- - } - if color.B > 0 { - color.B-- - } else { - pixel.hp-- - } - } - - if pixel.hp <= 0 { - pixel.breath = true - } else if pixel.hp >= 100 { - pixel.breath = false - } - return true - }) -} - -// Note: amount of memory allocated changes between tests even with deterministic rand. -// Observed range 918063 B/op - 1108007 B/op -func BenchmarkRangeIteration(b *testing.B) { - pixelSys := CreateSystemService(new(pixelSystem)) - world := PrepareWorld("range iteration", &pixelSys) - - b.ReportAllocs() - b.ResetTimer() - for range b.N { - world.runSystemFunction(SystemFunctionFixedUpdate) - } -} - -// Note: amount of memory allocated changes between tests even with deterministic rand. -// Observed range 868437 B/op - 1047789 B/op -func BenchmarkDirectCallIteration(b *testing.B) { - pixelSys := CreateSystemService(new(pixelSystemDirectCall)) - world := PrepareWorld("direct call iteration", &pixelSys) - - b.ReportAllocs() - b.ResetTimer() - for range b.N { - world.runSystemFunction(SystemFunctionFixedUpdate) - } -} diff --git a/stdcomponents/animation-player.go b/stdcomponents/animation-player.go index bb2ca5b8..420f92d7 100644 --- a/stdcomponents/animation-player.go +++ b/stdcomponents/animation-player.go @@ -35,5 +35,5 @@ type AnimationPlayer struct { type AnimationPlayerComponentManager = ecs.ComponentManager[AnimationPlayer] func NewAnimationPlayerComponentManager() AnimationPlayerComponentManager { - return ecs.NewComponentManager[AnimationPlayer](ANIMATION_PLAYER_ID) + return ecs.NewComponentManager[AnimationPlayer](AnimationPlayerComponentId) } diff --git a/stdcomponents/animation-state.go b/stdcomponents/animation-state.go index 4c3b20c3..e88f2b9b 100644 --- a/stdcomponents/animation-state.go +++ b/stdcomponents/animation-state.go @@ -23,5 +23,5 @@ type AnimationState int32 type AnimationStateComponentManager = ecs.ComponentManager[AnimationState] func NewAnimationStateComponentManager() AnimationStateComponentManager { - return ecs.NewComponentManager[AnimationState](ANIMATION_STATE_ID) + return ecs.NewComponentManager[AnimationState](AnimationStateComponentId) } diff --git a/stdcomponents/flip.go b/stdcomponents/flip.go index ae6d797f..32775772 100644 --- a/stdcomponents/flip.go +++ b/stdcomponents/flip.go @@ -23,5 +23,5 @@ type Flip struct { type FlipComponentManager = ecs.ComponentManager[Flip] func NewFlipComponentManager() FlipComponentManager { - return ecs.NewComponentManager[Flip](FLIP_ID) + return ecs.NewComponentManager[Flip](FlipComponentId) } diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index e005bc59..8626b595 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -11,18 +11,18 @@ import ( ) const ( - INVALID_ID ecs.ComponentId = iota + 128 - POSITION_ID - ROTATION_ID - SCALE_ID - FLIP_ID - VELOCITY_ID - SPRITE_ID - SPRITE_SHEET_ID - SPRITE_MATRIX_ID - TEXTURE_RENDER_ID - ANIMATION_PLAYER_ID - ANIMATION_STATE_ID - TINT_ID - NETWORK_ID + InvalidComponentId ecs.ComponentId = 1<<16 - 1 - iota + PositionComponentId + RotationComponentId + ScaleComponentId + FlipComponentId + VelocityComponentId + SpriteComponentId + SpriteSheetComponentId + SpriteMatrixComponentId + TextureRenderComponentId + AnimationPlayerComponentId + AnimationStateComponentId + TintComponentId + NetworkComponentId ) diff --git a/stdcomponents/network.go b/stdcomponents/network.go index 98b8d14f..558db91b 100644 --- a/stdcomponents/network.go +++ b/stdcomponents/network.go @@ -26,5 +26,5 @@ type Network struct { type NetworkComponentManager = ecs.ComponentManager[Network] func NewNetworkComponentManager() NetworkComponentManager { - return ecs.NewComponentManager[Network](NETWORK_ID) + return ecs.NewComponentManager[Network](NetworkComponentId) } diff --git a/stdcomponents/position.go b/stdcomponents/position.go index 655d7fa2..d04f258c 100644 --- a/stdcomponents/position.go +++ b/stdcomponents/position.go @@ -23,5 +23,5 @@ type Position struct { type PositionComponentManager = ecs.ComponentManager[Position] func NewPositionComponentManager() PositionComponentManager { - return ecs.NewComponentManager[Position](POSITION_ID) + return ecs.NewComponentManager[Position](PositionComponentId) } diff --git a/stdcomponents/rotation.go b/stdcomponents/rotation.go index ad6eaaa3..b2133119 100644 --- a/stdcomponents/rotation.go +++ b/stdcomponents/rotation.go @@ -23,5 +23,5 @@ type Rotation struct { type RotationComponentManager = ecs.ComponentManager[Rotation] func NewRotationComponentManager() RotationComponentManager { - return ecs.NewComponentManager[Rotation](ROTATION_ID) + return ecs.NewComponentManager[Rotation](RotationComponentId) } diff --git a/stdcomponents/scale.go b/stdcomponents/scale.go index 6f1dd41d..13b1593f 100644 --- a/stdcomponents/scale.go +++ b/stdcomponents/scale.go @@ -23,5 +23,5 @@ type Scale struct { type ScaleComponentManager = ecs.ComponentManager[Scale] func NewScaleComponentManager() ScaleComponentManager { - return ecs.NewComponentManager[Scale](SCALE_ID) + return ecs.NewComponentManager[Scale](ScaleComponentId) } diff --git a/stdcomponents/sprite-matrix.go b/stdcomponents/sprite-matrix.go index ea1cc2ed..2d71e5f6 100644 --- a/stdcomponents/sprite-matrix.go +++ b/stdcomponents/sprite-matrix.go @@ -37,5 +37,5 @@ type SpriteMatrix struct { type SpriteMatrixComponentManager = ecs.ComponentManager[SpriteMatrix] func NewSpriteMatrixComponentManager() SpriteMatrixComponentManager { - return ecs.NewComponentManager[SpriteMatrix](SPRITE_MATRIX_ID) + return ecs.NewComponentManager[SpriteMatrix](SpriteMatrixComponentId) } diff --git a/stdcomponents/sprite-sheet.go b/stdcomponents/sprite-sheet.go index 33ca57d1..3017e6f5 100644 --- a/stdcomponents/sprite-sheet.go +++ b/stdcomponents/sprite-sheet.go @@ -31,5 +31,5 @@ type SpriteSheet struct { type SpriteSheetComponentManager = ecs.ComponentManager[SpriteSheet] func NewSpriteSheetComponentManager() SpriteSheetComponentManager { - return ecs.NewComponentManager[SpriteSheet](SPRITE_SHEET_ID) + return ecs.NewComponentManager[SpriteSheet](SpriteSheetComponentId) } diff --git a/stdcomponents/sprite.go b/stdcomponents/sprite.go index 8656ef8e..80e9ead4 100644 --- a/stdcomponents/sprite.go +++ b/stdcomponents/sprite.go @@ -30,5 +30,5 @@ type Sprite struct { type SpriteComponentManager = ecs.ComponentManager[Sprite] func NewSpriteComponentManager() SpriteComponentManager { - return ecs.NewComponentManager[Sprite](SPRITE_ID) + return ecs.NewComponentManager[Sprite](SpriteComponentId) } diff --git a/stdcomponents/texture-render.go b/stdcomponents/texture-render.go index 96cbf457..a35f13de 100644 --- a/stdcomponents/texture-render.go +++ b/stdcomponents/texture-render.go @@ -32,5 +32,5 @@ type TextureRender struct { type TextureRenderComponentManager = ecs.ComponentManager[TextureRender] func NewTextureRenderComponentManager() TextureRenderComponentManager { - return ecs.NewComponentManager[TextureRender](TEXTURE_RENDER_ID) + return ecs.NewComponentManager[TextureRender](TextureRenderComponentId) } diff --git a/stdcomponents/tint.go b/stdcomponents/tint.go index d405a100..6fd93208 100644 --- a/stdcomponents/tint.go +++ b/stdcomponents/tint.go @@ -24,5 +24,5 @@ type Tint = color.RGBA type TintComponentManager = ecs.ComponentManager[Tint] func NewTintComponentManager() TintComponentManager { - return ecs.NewComponentManager[Tint](TINT_ID) + return ecs.NewComponentManager[Tint](TintComponentId) } diff --git a/stdcomponents/velocity.go b/stdcomponents/velocity.go index baafc378..059c8e00 100644 --- a/stdcomponents/velocity.go +++ b/stdcomponents/velocity.go @@ -23,5 +23,5 @@ type Velocity struct { type VelocityComponentManager = ecs.ComponentManager[Velocity] func NewVelocityComponentManager() VelocityComponentManager { - return ecs.NewComponentManager[Velocity](VELOCITY_ID) + return ecs.NewComponentManager[Velocity](VelocityComponentId) } From 547d9f0c909d303ba813fa7f0c1bad3351574fc4 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 22 Feb 2025 07:42:27 +0300 Subject: [PATCH 016/196] new engine runner --- engine.go | 65 ++++++++++---------- examples/new-api/game.go | 2 +- examples/new-api/instances/component-list.go | 2 + examples/new-api/instances/system-list.go | 2 + examples/new-api/scenes/main-scene.go | 39 ++++++------ examples/new-api/systems/player.go | 3 +- game.go | 13 +++- scene.go | 2 +- stdcomponents/ids.go | 1 + stdcomponents/view-position.go | 28 +++++++++ stdsystems/animation-player.go | 9 ++- stdsystems/animation-spritematrix.go | 2 +- stdsystems/asset-library.go | 3 +- stdsystems/debug.go | 6 +- stdsystems/render.go | 12 +--- stdsystems/texture-render-animation.go | 3 +- stdsystems/texture-render-flip.go | 3 +- stdsystems/texture-render-position.go | 16 ++--- stdsystems/texture-render-rotation.go | 3 +- stdsystems/texture-render-scale.go | 3 +- stdsystems/texture-render-sprite.go | 3 +- stdsystems/texture-render-spritematrix.go | 3 +- stdsystems/texture-render-spritesheet.go | 3 +- stdsystems/texture-render-tint.go | 3 +- stdsystems/view-position.go | 48 +++++++++++++++ 25 files changed, 175 insertions(+), 102 deletions(-) create mode 100644 stdcomponents/view-position.go create mode 100644 stdsystems/view-position.go diff --git a/engine.go b/engine.go index 822cf958..e81fc28c 100644 --- a/engine.go +++ b/engine.go @@ -15,17 +15,13 @@ Thank you for your support! package gomp import ( + "log" "time" ) -type AnyGame interface { - Init() - Update(dt time.Duration) - FixedUpdate(dt time.Duration) - Render(dt time.Duration) - Destroy() - ShouldDestroy() bool -} +const ( + MaxFrameSkips = 10 +) func NewEngine(game AnyGame) Engine { engine := Engine{ @@ -38,49 +34,50 @@ func NewEngine(game AnyGame) Engine { type Engine struct { Game AnyGame - lastUpdateAt time.Time lastFixedUpdateAt time.Time - lastRenderAt time.Time } func (e *Engine) Run(tickrate uint, framerate uint) { - fixedUpdDuration := time.Second / time.Duration(tickrate) - framerateDuration := time.Second / time.Duration(framerate) - fixedUpdTicker := time.NewTicker(fixedUpdDuration) - defer fixedUpdTicker.Stop() + fixedUpdDuration := time.Second / time.Duration(tickrate) - renderTicker := time.NewTicker(framerateDuration) - defer renderTicker.Stop() + var renderTicker *time.Ticker + if framerate > 0 { + renderTicker = time.NewTicker(time.Second / time.Duration(framerate)) + defer renderTicker.Stop() + } e.Game.Init() defer e.Game.Destroy() - e.lastUpdateAt = time.Now() - e.lastFixedUpdateAt = time.Now() - e.lastRenderAt = time.Now() + lastUpdateAt := time.Now() // TODO: REMOVE? + nextFixedUpdateAt := time.Now() for !e.Game.ShouldDestroy() { + if renderTicker != nil { + <-renderTicker.C + } + // Update - e.Game.Update(time.Since(e.lastUpdateAt)) - e.lastUpdateAt = time.Now() + e.Game.Update(time.Since(lastUpdateAt)) // Fixed Update - select { - case <-fixedUpdTicker.C: - e.Game.FixedUpdate(time.Since(e.lastFixedUpdateAt)) - e.lastFixedUpdateAt = time.Now() - default: - break + loops := 0 + for nextFixedUpdateAt.Compare(time.Now()) == -1 && loops < MaxFrameSkips { + e.Game.FixedUpdate(fixedUpdDuration) + e.lastFixedUpdateAt = nextFixedUpdateAt + nextFixedUpdateAt = nextFixedUpdateAt.Add(fixedUpdDuration) + loops++ + } + if loops >= MaxFrameSkips { + log.Println("Too many updates detected") } // Render - select { - case <-renderTicker.C: - e.Game.Render(time.Since(e.lastRenderAt)) - e.lastRenderAt = time.Now() - default: - break - } + + sinceLastFixedUpdateAt := time.Since(e.lastFixedUpdateAt) + interpolation := float32(sinceLastFixedUpdateAt.Microseconds()) / float32(fixedUpdDuration.Microseconds()) + e.Game.Render(interpolation) + lastUpdateAt = time.Now() } } diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 38cdf812..c4ad60a6 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -28,5 +28,5 @@ func main() { game.CurrentSceneId = scenes.MainSceneId engine := gomp.NewEngine(&game) - engine.Run(30, 165) + engine.Run(50, 165) } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index b0c734c9..fc5e0906 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -23,6 +23,7 @@ type ComponentList struct { Position stdcomponents.PositionComponentManager Rotation stdcomponents.RotationComponentManager Scale stdcomponents.ScaleComponentManager + ViewPosition stdcomponents.ViewPositionComponentManager Velocity stdcomponents.VelocityComponentManager Flip stdcomponents.FlipComponentManager Sprite stdcomponents.SpriteComponentManager @@ -43,6 +44,7 @@ func NewComponentList() ComponentList { Position: stdcomponents.NewPositionComponentManager(), Rotation: stdcomponents.NewRotationComponentManager(), Scale: stdcomponents.NewScaleComponentManager(), + ViewPosition: stdcomponents.NewViewPositionComponentManager(), Velocity: stdcomponents.NewVelocityComponentManager(), Flip: stdcomponents.NewFlipComponentManager(), Sprite: stdcomponents.NewSpriteComponentManager(), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index cf1b473f..9040a2cc 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -24,6 +24,7 @@ import ( func NewSystemList() SystemList { newSystemList := SystemList{ Player: systems.NewPlayerSystem(), + ViewPosition: stdsystems.NewViewPositionSystem(), Debug: stdsystems.NewDebugSystem(), Velocity: stdsystems.NewVelocitySystem(), Network: stdsystems.NewNetworkSystem(), @@ -49,6 +50,7 @@ func NewSystemList() SystemList { type SystemList struct { Player systems.PlayerSystem + ViewPosition stdsystems.ViewPositionSystem Debug stdsystems.DebugSystem Velocity stdsystems.VelocitySystem Network stdsystems.NetworkSystem diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index a24dc007..d443f57b 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -77,44 +77,43 @@ func (s *MainScene) Init() { func (s *MainScene) Update(dt time.Duration) gomp.SceneId { // Network receive s.World.Systems.NetworkReceive.Run(dt) + s.World.Systems.Player.Run() return MainSceneId } func (s *MainScene) FixedUpdate(dt time.Duration) { // Network send - s.World.Systems.NetworkSend.Run(dt) -} - -func (s *MainScene) Render(dt time.Duration) { s.World.Systems.Network.Run(dt) - s.World.Systems.Player.Run(dt) - s.World.Systems.Velocity.Run(dt) + s.World.Systems.ViewPosition.Run() + s.World.Systems.NetworkSend.Run(dt) +} +func (s *MainScene) Render(interpolation float32) { // Animation - s.World.Systems.AnimationSpriteMatrix.Run(dt) - s.World.Systems.AnimationPlayer.Run(dt) + s.World.Systems.AnimationSpriteMatrix.Run() + s.World.Systems.AnimationPlayer.Run() // Prerender init - s.World.Systems.TextureRenderSprite.Run(dt) - s.World.Systems.TextureRenderSpriteSheet.Run(dt) - s.World.Systems.TextureRenderMatrix.Run(dt) + s.World.Systems.TextureRenderSprite.Run() + s.World.Systems.TextureRenderSpriteSheet.Run() + s.World.Systems.TextureRenderMatrix.Run() // Prerender fill - s.World.Systems.TextureRenderAnimation.Run(dt) - s.World.Systems.TextureRenderFlip.Run(dt) - s.World.Systems.TextureRenderPosition.Run(dt) - s.World.Systems.TextureRenderRotation.Run(dt) - s.World.Systems.TextureRenderScale.Run(dt) - s.World.Systems.TextureRenderTint.Run(dt) + s.World.Systems.TextureRenderAnimation.Run() + s.World.Systems.TextureRenderFlip.Run() + s.World.Systems.TextureRenderPosition.Run(interpolation) + s.World.Systems.TextureRenderRotation.Run(interpolation) + s.World.Systems.TextureRenderScale.Run(interpolation) + s.World.Systems.TextureRenderTint.Run(interpolation) // Render - s.World.Systems.Debug.Run(dt) + s.World.Systems.Debug.Run() - s.World.Systems.AssetLib.Run(dt) - shouldContinue := s.World.Systems.Render.Run(dt) + s.World.Systems.AssetLib.Run() + shouldContinue := s.World.Systems.Render.Run() if !shouldContinue { s.Game.SetShouldDestroy(true) return diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 28fcb056..c8002ca0 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -12,7 +12,6 @@ import ( "gomp/examples/new-api/entities" "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewPlayerSystem() PlayerSystem { @@ -46,7 +45,7 @@ func (s *PlayerSystem) Init() { s.Player.Position.X = 100 s.Player.Position.Y = 100 } -func (s *PlayerSystem) Run(dt time.Duration) { +func (s *PlayerSystem) Run() { animationState := s.AnimationStates.Get(s.Player.Entity) var speed float32 = 300 diff --git a/game.go b/game.go index 10acc006..bf062c13 100644 --- a/game.go +++ b/game.go @@ -20,6 +20,15 @@ import ( "time" ) +type AnyGame interface { + Init() + Update(dt time.Duration) + FixedUpdate(dt time.Duration) + Render(interpolation float32) + Destroy() + ShouldDestroy() bool +} + func NewGame(scenes ...AnyScene) Game { sceneSet := make(map[SceneId]AnyScene, len(scenes)) @@ -70,10 +79,10 @@ func (g *Game) FixedUpdate(dt time.Duration) { scene.FixedUpdate(dt) } -func (g *Game) Render(dt time.Duration) { +func (g *Game) Render(interpolation float32) { scene, ok := g.Scenes[g.CurrentSceneId] assert.True(ok, "Scene not found") - scene.Render(dt) + scene.Render(interpolation) } func (g *Game) Destroy() { diff --git a/scene.go b/scene.go index 012b9dda..58c436f6 100644 --- a/scene.go +++ b/scene.go @@ -24,7 +24,7 @@ type AnyScene interface { Init() Update(dt time.Duration) SceneId FixedUpdate(dt time.Duration) - Render(dt time.Duration) + Render(interpolation float32) Destroy() OnEnter() OnExit() diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 8626b595..ecfd90ab 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -15,6 +15,7 @@ const ( PositionComponentId RotationComponentId ScaleComponentId + ViewPositionId FlipComponentId VelocityComponentId SpriteComponentId diff --git a/stdcomponents/view-position.go b/stdcomponents/view-position.go new file mode 100644 index 00000000..564d9290 --- /dev/null +++ b/stdcomponents/view-position.go @@ -0,0 +1,28 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type ViewPosition struct { + LastPositionX, LastPositionY float32 + CurrentPositionX, CurrentPositionY float32 +} + +type ViewPositionComponentManager = ecs.ComponentManager[ViewPosition] + +func NewViewPositionComponentManager() ViewPositionComponentManager { + return ecs.NewComponentManager[ViewPosition](ViewPositionId) +} diff --git a/stdsystems/animation-player.go b/stdsystems/animation-player.go index 157e55cb..91d41d42 100644 --- a/stdsystems/animation-player.go +++ b/stdsystems/animation-player.go @@ -21,10 +21,14 @@ func NewAnimationPlayerSystem() AnimationPlayerSystem { type AnimationPlayerSystem struct { AnimationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] + lastRunAt time.Time } -func (s *AnimationPlayerSystem) Init() {} -func (s *AnimationPlayerSystem) Run(dt time.Duration) { +func (s *AnimationPlayerSystem) Init() { + s.lastRunAt = time.Now() +} +func (s *AnimationPlayerSystem) Run() { + dt := time.Since(s.lastRunAt) s.AnimationPlayers.AllDataParallel(func(animation *stdcomponents.AnimationPlayer) bool { animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond @@ -61,5 +65,6 @@ func (s *AnimationPlayerSystem) Run(dt time.Duration) { return true }) + s.lastRunAt = time.Now() } func (s *AnimationPlayerSystem) Destroy() {} diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index f328c3fa..9ff36375 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -24,7 +24,7 @@ type AnimationSpriteMatrixSystem struct { } func (s *AnimationSpriteMatrixSystem) Init() {} -func (s *AnimationSpriteMatrixSystem) Run(dt time.Duration) { +func (s *AnimationSpriteMatrixSystem) Run() { s.AnimationPlayers.AllParallel(func(e ecs.Entity, animationPlayer *stdcomponents.AnimationPlayer) bool { spriteMatrix := s.SpriteMatrixes.Get(e) if spriteMatrix == nil { diff --git a/stdsystems/asset-library.go b/stdsystems/asset-library.go index 6052ac2d..b640b233 100644 --- a/stdsystems/asset-library.go +++ b/stdsystems/asset-library.go @@ -8,7 +8,6 @@ package stdsystems import ( "gomp" - "time" ) func NewAssetLibSystem(assets []gomp.AnyAssetLibrary) AssetLibSystem { @@ -22,7 +21,7 @@ type AssetLibSystem struct { } func (s *AssetLibSystem) Init() {} -func (s *AssetLibSystem) Run(dt time.Duration) { +func (s *AssetLibSystem) Run() { for _, asset := range s.assets { asset.LoadAll() } diff --git a/stdsystems/debug.go b/stdsystems/debug.go index b4967c27..026ff089 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -8,12 +8,10 @@ package stdsystems import ( "fmt" + rl "github.com/gen2brain/raylib-go/raylib" "log" "os" "runtime/pprof" - "time" - - rl "github.com/gen2brain/raylib-go/raylib" ) func NewDebugSystem() DebugSystem { @@ -25,7 +23,7 @@ type DebugSystem struct { } func (s *DebugSystem) Init() {} -func (s *DebugSystem) Run(dt time.Duration) { +func (s *DebugSystem) Run() { if rl.IsKeyPressed(rl.KeyF9) { if s.pprofEnabled { pprof.StopCPUProfile() diff --git a/stdsystems/render.go b/stdsystems/render.go index 6be87b5b..6a241aaf 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -11,7 +11,6 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewRenderSystem() RenderSystem { @@ -27,9 +26,9 @@ type RenderSystem struct { func (s *RenderSystem) Init() { rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") //currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) - //rl.SetTargetFPS(currentMonitorRefreshRate) + //rl.SetTargetFPS(12) } -func (s *RenderSystem) Run(dt time.Duration) bool { +func (s *RenderSystem) Run() bool { if rl.WindowShouldClose() { return false } @@ -47,13 +46,6 @@ func (s *RenderSystem) Run(dt time.Duration) bool { rl.DrawFPS(10, 10) rl.DrawText(fmt.Sprintf("%d", s.EntityManager.Size()), 10, 30, 20, rl.Red) - s.Positions.AllData(func(p *stdcomponents.Position) bool { - rl.DrawCircle(int32(p.X), int32(p.Y), 10, rl.Red) - return true - }) - - rl.DrawText(fmt.Sprintf("%s", dt), 10, 50, 20, rl.Red) - rl.EndDrawing() return true } diff --git a/stdsystems/texture-render-animation.go b/stdsystems/texture-render-animation.go index 8781aed4..60cd25f7 100644 --- a/stdsystems/texture-render-animation.go +++ b/stdsystems/texture-render-animation.go @@ -9,7 +9,6 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderAnimationSystem() TextureRenderAnimationSystem { @@ -23,7 +22,7 @@ type TextureRenderAnimationSystem struct { } func (s *TextureRenderAnimationSystem) Init() {} -func (s *TextureRenderAnimationSystem) Run(dt time.Duration) { +func (s *TextureRenderAnimationSystem) Run() { // Run sprites and spriteRenders s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { diff --git a/stdsystems/texture-render-flip.go b/stdsystems/texture-render-flip.go index e3cf88e7..d449db06 100644 --- a/stdsystems/texture-render-flip.go +++ b/stdsystems/texture-render-flip.go @@ -9,7 +9,6 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderFlipSystem() TextureRenderFlipSystem { @@ -23,7 +22,7 @@ type TextureRenderFlipSystem struct { } func (s *TextureRenderFlipSystem) Init() {} -func (s *TextureRenderFlipSystem) Run(dt time.Duration) { +func (s *TextureRenderFlipSystem) Run() { // Run sprites and spriteRenders s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { diff --git a/stdsystems/texture-render-position.go b/stdsystems/texture-render-position.go index dab1feba..f92c9bfa 100644 --- a/stdsystems/texture-render-position.go +++ b/stdsystems/texture-render-position.go @@ -9,7 +9,6 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderPositionSystem() TextureRenderPositionSystem { @@ -18,24 +17,27 @@ func NewTextureRenderPositionSystem() TextureRenderPositionSystem { // TextureRenderPositionSystem is a system that sets Position of textureRender type TextureRenderPositionSystem struct { - Positions *stdcomponents.PositionComponentManager + ViewPositions *stdcomponents.ViewPositionComponentManager TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderPositionSystem) Init() {} -func (s *TextureRenderPositionSystem) Run(dt time.Duration) { +func (s *TextureRenderPositionSystem) Run(interpolation float32) { s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true } - position := s.Positions.Get(entity) - if position == nil { + viewPosition := s.ViewPositions.Get(entity) + if viewPosition == nil { return true } - tr.Dest.X = position.X - tr.Dest.Y = position.Y + // Lerp(start, end, amount float32) start + amount*(end-start) + interpolationX := interpolation * (viewPosition.CurrentPositionX - viewPosition.LastPositionX) + interpolationY := interpolation * (viewPosition.CurrentPositionY - viewPosition.LastPositionY) + tr.Dest.X = viewPosition.LastPositionX + interpolationX + tr.Dest.Y = viewPosition.LastPositionY + interpolationY return true }) diff --git a/stdsystems/texture-render-rotation.go b/stdsystems/texture-render-rotation.go index ae455feb..e17d8d75 100644 --- a/stdsystems/texture-render-rotation.go +++ b/stdsystems/texture-render-rotation.go @@ -9,7 +9,6 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderRotationSystem() TextureRenderRotationSystem { @@ -23,7 +22,7 @@ type TextureRenderRotationSystem struct { } func (s *TextureRenderRotationSystem) Init() {} -func (s *TextureRenderRotationSystem) Run(dt time.Duration) { +func (s *TextureRenderRotationSystem) Run(interpolation float32) { // Run sprites and spriteRenders s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { diff --git a/stdsystems/texture-render-scale.go b/stdsystems/texture-render-scale.go index 91fa4335..b159efbf 100644 --- a/stdsystems/texture-render-scale.go +++ b/stdsystems/texture-render-scale.go @@ -9,7 +9,6 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderScaleSystem() TextureRenderScaleSystem { @@ -23,7 +22,7 @@ type TextureRenderScaleSystem struct { } func (s *TextureRenderScaleSystem) Init() {} -func (s *TextureRenderScaleSystem) Run(dt time.Duration) { +func (s *TextureRenderScaleSystem) Run(interpolation float32) { s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true diff --git a/stdsystems/texture-render-sprite.go b/stdsystems/texture-render-sprite.go index 5977b0a7..f6bc6f98 100644 --- a/stdsystems/texture-render-sprite.go +++ b/stdsystems/texture-render-sprite.go @@ -18,7 +18,6 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderSpriteSystem() TextureRenderSpriteSystem { @@ -32,7 +31,7 @@ type TextureRenderSpriteSystem struct { } func (s *TextureRenderSpriteSystem) Init() {} -func (s *TextureRenderSpriteSystem) Run(dt time.Duration) { +func (s *TextureRenderSpriteSystem) Run() { // Run sprites and spriteRenders s.Sprites.AllParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { if sprite == nil { diff --git a/stdsystems/texture-render-spritematrix.go b/stdsystems/texture-render-spritematrix.go index 80774290..49440f9d 100644 --- a/stdsystems/texture-render-spritematrix.go +++ b/stdsystems/texture-render-spritematrix.go @@ -10,7 +10,6 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderMatrixSystem() TextureRenderMatrixSystem { @@ -25,7 +24,7 @@ type TextureRenderMatrixSystem struct { } func (s *TextureRenderMatrixSystem) Init() {} -func (s *TextureRenderMatrixSystem) Run(dt time.Duration) { +func (s *TextureRenderMatrixSystem) Run() { // Run sprites and spriteRenders s.SpriteMatrixes.AllParallel(func(entity ecs.Entity, spriteMatrix *stdcomponents.SpriteMatrix) bool { if spriteMatrix == nil { diff --git a/stdsystems/texture-render-spritesheet.go b/stdsystems/texture-render-spritesheet.go index eb2b8d7a..9c53b098 100644 --- a/stdsystems/texture-render-spritesheet.go +++ b/stdsystems/texture-render-spritesheet.go @@ -10,7 +10,6 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderSpriteSheetSystem() TextureRenderSpriteSheetSystem { @@ -24,7 +23,7 @@ type TextureRenderSpriteSheetSystem struct { } func (s *TextureRenderSpriteSheetSystem) Init() {} -func (s *TextureRenderSpriteSheetSystem) Run(dt time.Duration) { +func (s *TextureRenderSpriteSheetSystem) Run() { s.SpriteSheets.AllParallel(func(entity ecs.Entity, spriteSheet *stdcomponents.SpriteSheet) bool { if spriteSheet == nil { return true diff --git a/stdsystems/texture-render-tint.go b/stdsystems/texture-render-tint.go index 6089054f..d508464a 100644 --- a/stdsystems/texture-render-tint.go +++ b/stdsystems/texture-render-tint.go @@ -9,7 +9,6 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" - "time" ) func NewTextureRenderTintSystem() TextureRenderTintSystem { @@ -23,7 +22,7 @@ type TextureRenderTintSystem struct { } func (s *TextureRenderTintSystem) Init() {} -func (s *TextureRenderTintSystem) Run(dt time.Duration) { +func (s *TextureRenderTintSystem) Run(interpolation float32) { s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true diff --git a/stdsystems/view-position.go b/stdsystems/view-position.go new file mode 100644 index 00000000..66ef0d6d --- /dev/null +++ b/stdsystems/view-position.go @@ -0,0 +1,48 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +func NewViewPositionSystem() ViewPositionSystem { + return ViewPositionSystem{} +} + +type ViewPositionSystem struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + ViewPositions *stdcomponents.ViewPositionComponentManager +} + +func (s *ViewPositionSystem) Init() {} +func (s *ViewPositionSystem) Run() { + s.Positions.AllParallel(func(entity ecs.Entity, p *stdcomponents.Position) bool { + viewPosition := s.ViewPositions.Get(entity) + + if viewPosition == nil { + viewPosition = s.ViewPositions.Create(entity, stdcomponents.ViewPosition{}) + } + + viewPosition.LastPositionX = viewPosition.CurrentPositionX + viewPosition.LastPositionY = viewPosition.CurrentPositionY + viewPosition.CurrentPositionX = p.X + viewPosition.CurrentPositionY = p.Y + return true + }) +} +func (s *ViewPositionSystem) Destroy() {} From e8455540af2ad34d246a16d4e4952b6971d550f4 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 22 Feb 2025 20:19:01 +0300 Subject: [PATCH 017/196] refactor render lerp with exp decay function --- engine.go | 18 ++++---- examples/new-api/game.go | 2 +- examples/new-api/instances/component-list.go | 2 - examples/new-api/instances/system-list.go | 2 - examples/new-api/scenes/main-scene.go | 11 ++--- game.go | 6 +-- scene.go | 2 +- stdcomponents/view-position.go | 28 ------------ stdsystems/render.go | 5 ++ stdsystems/texture-render-position.go | 22 +++++---- stdsystems/texture-render-rotation.go | 2 +- stdsystems/texture-render-scale.go | 2 +- stdsystems/texture-render-spritematrix.go | 14 ++++-- stdsystems/texture-render-tint.go | 2 +- stdsystems/view-position.go | 48 -------------------- 15 files changed, 51 insertions(+), 115 deletions(-) delete mode 100644 stdcomponents/view-position.go delete mode 100644 stdsystems/view-position.go diff --git a/engine.go b/engine.go index e81fc28c..2ff8ad05 100644 --- a/engine.go +++ b/engine.go @@ -50,19 +50,23 @@ func (e *Engine) Run(tickrate uint, framerate uint) { e.Game.Init() defer e.Game.Destroy() - lastUpdateAt := time.Now() // TODO: REMOVE? - nextFixedUpdateAt := time.Now() + var lastUpdateAt = time.Now() // TODO: REMOVE? + var nextFixedUpdateAt = time.Now() + var dt = time.Since(lastUpdateAt) for !e.Game.ShouldDestroy() { if renderTicker != nil { <-renderTicker.C } + dt = time.Since(lastUpdateAt) + lastUpdateAt = time.Now() // Update - e.Game.Update(time.Since(lastUpdateAt)) + e.Game.Update(dt) // Fixed Update loops := 0 + // TODO: Refactor to work without for loop for nextFixedUpdateAt.Compare(time.Now()) == -1 && loops < MaxFrameSkips { e.Game.FixedUpdate(fixedUpdDuration) e.lastFixedUpdateAt = nextFixedUpdateAt @@ -74,10 +78,8 @@ func (e *Engine) Run(tickrate uint, framerate uint) { } // Render - - sinceLastFixedUpdateAt := time.Since(e.lastFixedUpdateAt) - interpolation := float32(sinceLastFixedUpdateAt.Microseconds()) / float32(fixedUpdDuration.Microseconds()) - e.Game.Render(interpolation) - lastUpdateAt = time.Now() + //sinceLastFixedUpdateAt := time.Since(e.lastFixedUpdateAt) + //interpolation := float32(sinceLastFixedUpdateAt.Microseconds()) / float32(fixedUpdDuration.Microseconds()) + e.Game.Render(dt) } } diff --git a/examples/new-api/game.go b/examples/new-api/game.go index c4ad60a6..27880733 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -28,5 +28,5 @@ func main() { game.CurrentSceneId = scenes.MainSceneId engine := gomp.NewEngine(&game) - engine.Run(50, 165) + engine.Run(50, 600) } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index fc5e0906..b0c734c9 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -23,7 +23,6 @@ type ComponentList struct { Position stdcomponents.PositionComponentManager Rotation stdcomponents.RotationComponentManager Scale stdcomponents.ScaleComponentManager - ViewPosition stdcomponents.ViewPositionComponentManager Velocity stdcomponents.VelocityComponentManager Flip stdcomponents.FlipComponentManager Sprite stdcomponents.SpriteComponentManager @@ -44,7 +43,6 @@ func NewComponentList() ComponentList { Position: stdcomponents.NewPositionComponentManager(), Rotation: stdcomponents.NewRotationComponentManager(), Scale: stdcomponents.NewScaleComponentManager(), - ViewPosition: stdcomponents.NewViewPositionComponentManager(), Velocity: stdcomponents.NewVelocityComponentManager(), Flip: stdcomponents.NewFlipComponentManager(), Sprite: stdcomponents.NewSpriteComponentManager(), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index 9040a2cc..cf1b473f 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -24,7 +24,6 @@ import ( func NewSystemList() SystemList { newSystemList := SystemList{ Player: systems.NewPlayerSystem(), - ViewPosition: stdsystems.NewViewPositionSystem(), Debug: stdsystems.NewDebugSystem(), Velocity: stdsystems.NewVelocitySystem(), Network: stdsystems.NewNetworkSystem(), @@ -50,7 +49,6 @@ func NewSystemList() SystemList { type SystemList struct { Player systems.PlayerSystem - ViewPosition stdsystems.ViewPositionSystem Debug stdsystems.DebugSystem Velocity stdsystems.VelocitySystem Network stdsystems.NetworkSystem diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index d443f57b..ab282c8a 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -87,11 +87,10 @@ func (s *MainScene) FixedUpdate(dt time.Duration) { s.World.Systems.Network.Run(dt) s.World.Systems.Velocity.Run(dt) - s.World.Systems.ViewPosition.Run() s.World.Systems.NetworkSend.Run(dt) } -func (s *MainScene) Render(interpolation float32) { +func (s *MainScene) Render(dt time.Duration) { // Animation s.World.Systems.AnimationSpriteMatrix.Run() s.World.Systems.AnimationPlayer.Run() @@ -104,10 +103,10 @@ func (s *MainScene) Render(interpolation float32) { // Prerender fill s.World.Systems.TextureRenderAnimation.Run() s.World.Systems.TextureRenderFlip.Run() - s.World.Systems.TextureRenderPosition.Run(interpolation) - s.World.Systems.TextureRenderRotation.Run(interpolation) - s.World.Systems.TextureRenderScale.Run(interpolation) - s.World.Systems.TextureRenderTint.Run(interpolation) + s.World.Systems.TextureRenderPosition.Run(dt) + s.World.Systems.TextureRenderRotation.Run() + s.World.Systems.TextureRenderScale.Run() + s.World.Systems.TextureRenderTint.Run() // Render s.World.Systems.Debug.Run() diff --git a/game.go b/game.go index bf062c13..8104fe07 100644 --- a/game.go +++ b/game.go @@ -24,7 +24,7 @@ type AnyGame interface { Init() Update(dt time.Duration) FixedUpdate(dt time.Duration) - Render(interpolation float32) + Render(dt time.Duration) Destroy() ShouldDestroy() bool } @@ -79,10 +79,10 @@ func (g *Game) FixedUpdate(dt time.Duration) { scene.FixedUpdate(dt) } -func (g *Game) Render(interpolation float32) { +func (g *Game) Render(dt time.Duration) { scene, ok := g.Scenes[g.CurrentSceneId] assert.True(ok, "Scene not found") - scene.Render(interpolation) + scene.Render(dt) } func (g *Game) Destroy() { diff --git a/scene.go b/scene.go index 58c436f6..012b9dda 100644 --- a/scene.go +++ b/scene.go @@ -24,7 +24,7 @@ type AnyScene interface { Init() Update(dt time.Duration) SceneId FixedUpdate(dt time.Duration) - Render(interpolation float32) + Render(dt time.Duration) Destroy() OnEnter() OnExit() diff --git a/stdcomponents/view-position.go b/stdcomponents/view-position.go deleted file mode 100644 index 564d9290..00000000 --- a/stdcomponents/view-position.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package stdcomponents - -import "gomp/pkg/ecs" - -type ViewPosition struct { - LastPositionX, LastPositionY float32 - CurrentPositionX, CurrentPositionY float32 -} - -type ViewPositionComponentManager = ecs.ComponentManager[ViewPosition] - -func NewViewPositionComponentManager() ViewPositionComponentManager { - return ecs.NewComponentManager[ViewPosition](ViewPositionId) -} diff --git a/stdsystems/render.go b/stdsystems/render.go index 6a241aaf..1c1c3e1b 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -42,6 +42,11 @@ func (s *RenderSystem) Run() bool { return true }) + s.Positions.AllData(func(position *stdcomponents.Position) bool { + rl.DrawRectangle(int32(position.X), int32(position.Y), 10, 10, rl.Red) + return true + }) + // rl.DrawRectangle(0, 0, 120, 120, rl.DarkGray) rl.DrawFPS(10, 10) rl.DrawText(fmt.Sprintf("%d", s.EntityManager.Size()), 10, 30, 20, rl.Red) diff --git a/stdsystems/texture-render-position.go b/stdsystems/texture-render-position.go index f92c9bfa..76b836ab 100644 --- a/stdsystems/texture-render-position.go +++ b/stdsystems/texture-render-position.go @@ -9,6 +9,8 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" + "math" + "time" ) func NewTextureRenderPositionSystem() TextureRenderPositionSystem { @@ -17,29 +19,31 @@ func NewTextureRenderPositionSystem() TextureRenderPositionSystem { // TextureRenderPositionSystem is a system that sets Position of textureRender type TextureRenderPositionSystem struct { - ViewPositions *stdcomponents.ViewPositionComponentManager + Positions *stdcomponents.PositionComponentManager TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderPositionSystem) Init() {} -func (s *TextureRenderPositionSystem) Run(interpolation float32) { +func (s *TextureRenderPositionSystem) Run(dt time.Duration) { s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true } - viewPosition := s.ViewPositions.Get(entity) - if viewPosition == nil { + position := s.Positions.Get(entity) + if position == nil { return true } - // Lerp(start, end, amount float32) start + amount*(end-start) - interpolationX := interpolation * (viewPosition.CurrentPositionX - viewPosition.LastPositionX) - interpolationY := interpolation * (viewPosition.CurrentPositionY - viewPosition.LastPositionY) - tr.Dest.X = viewPosition.LastPositionX + interpolationX - tr.Dest.Y = viewPosition.LastPositionY + interpolationY + decay := 100.0 + tr.Dest.X = float32(expDecay(float64(tr.Dest.X), float64(position.X), decay, dt)) + tr.Dest.Y = float32(expDecay(float64(tr.Dest.Y), float64(position.Y), decay, dt)) return true }) } func (s *TextureRenderPositionSystem) Destroy() {} + +func expDecay(a, b, decay float64, dt time.Duration) float64 { + return b + (a-b)*(math.Exp(-decay*dt.Seconds())) +} diff --git a/stdsystems/texture-render-rotation.go b/stdsystems/texture-render-rotation.go index e17d8d75..289b6ddd 100644 --- a/stdsystems/texture-render-rotation.go +++ b/stdsystems/texture-render-rotation.go @@ -22,7 +22,7 @@ type TextureRenderRotationSystem struct { } func (s *TextureRenderRotationSystem) Init() {} -func (s *TextureRenderRotationSystem) Run(interpolation float32) { +func (s *TextureRenderRotationSystem) Run() { // Run sprites and spriteRenders s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { diff --git a/stdsystems/texture-render-scale.go b/stdsystems/texture-render-scale.go index b159efbf..5ba21a9d 100644 --- a/stdsystems/texture-render-scale.go +++ b/stdsystems/texture-render-scale.go @@ -22,7 +22,7 @@ type TextureRenderScaleSystem struct { } func (s *TextureRenderScaleSystem) Init() {} -func (s *TextureRenderScaleSystem) Run(interpolation float32) { +func (s *TextureRenderScaleSystem) Run() { s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true diff --git a/stdsystems/texture-render-spritematrix.go b/stdsystems/texture-render-spritematrix.go index 49440f9d..7a4da991 100644 --- a/stdsystems/texture-render-spritematrix.go +++ b/stdsystems/texture-render-spritematrix.go @@ -21,6 +21,7 @@ type TextureRenderMatrixSystem struct { SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager TextureRenders *stdcomponents.TextureRenderComponentManager AnimationStates *stdcomponents.AnimationStateComponentManager + Positions *stdcomponents.PositionComponentManager } func (s *TextureRenderMatrixSystem) Init() {} @@ -36,6 +37,11 @@ func (s *TextureRenderMatrixSystem) Run() { return true } + position := s.Positions.Get(entity) + if position == nil { + return true + } + currentAnimationFrame := spriteMatrix.Animations[*animationState].Frame tr := s.TextureRenders.Get(entity) @@ -46,6 +52,8 @@ func (s *TextureRenderMatrixSystem) Run() { Origin: spriteMatrix.Origin, Frame: currentAnimationFrame, Dest: rl.Rectangle{ + X: position.X, + Y: position.Y, Width: currentAnimationFrame.Width, Height: currentAnimationFrame.Height, }, @@ -56,10 +64,8 @@ func (s *TextureRenderMatrixSystem) Run() { // Run spriteRender tr.Texture = spriteMatrix.Texture tr.Origin = spriteMatrix.Origin - tr.Dest = rl.Rectangle{ - Width: currentAnimationFrame.Width, - Height: currentAnimationFrame.Height, - } + tr.Dest.Width = currentAnimationFrame.Width + tr.Dest.Height = currentAnimationFrame.Height tr.Frame = currentAnimationFrame } return true diff --git a/stdsystems/texture-render-tint.go b/stdsystems/texture-render-tint.go index d508464a..7c027f83 100644 --- a/stdsystems/texture-render-tint.go +++ b/stdsystems/texture-render-tint.go @@ -22,7 +22,7 @@ type TextureRenderTintSystem struct { } func (s *TextureRenderTintSystem) Init() {} -func (s *TextureRenderTintSystem) Run(interpolation float32) { +func (s *TextureRenderTintSystem) Run() { s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true diff --git a/stdsystems/view-position.go b/stdsystems/view-position.go deleted file mode 100644 index 66ef0d6d..00000000 --- a/stdsystems/view-position.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewViewPositionSystem() ViewPositionSystem { - return ViewPositionSystem{} -} - -type ViewPositionSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - ViewPositions *stdcomponents.ViewPositionComponentManager -} - -func (s *ViewPositionSystem) Init() {} -func (s *ViewPositionSystem) Run() { - s.Positions.AllParallel(func(entity ecs.Entity, p *stdcomponents.Position) bool { - viewPosition := s.ViewPositions.Get(entity) - - if viewPosition == nil { - viewPosition = s.ViewPositions.Create(entity, stdcomponents.ViewPosition{}) - } - - viewPosition.LastPositionX = viewPosition.CurrentPositionX - viewPosition.LastPositionY = viewPosition.CurrentPositionY - viewPosition.CurrentPositionX = p.X - viewPosition.CurrentPositionY = p.Y - return true - }) -} -func (s *ViewPositionSystem) Destroy() {} From 997493da1dac7417cb5ebe7c99a3ab0e31c5e25d Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 23 Feb 2025 18:52:51 +0300 Subject: [PATCH 018/196] optimize some issues --- engine.go | 7 +------ examples/new-api/game.go | 2 +- examples/new-api/systems/player.go | 17 ++++++++++++----- stdsystems/animation-player.go | 3 +-- stdsystems/render.go | 23 +++++++++++++---------- stdsystems/texture-render-position.go | 12 +++++++----- 6 files changed, 35 insertions(+), 29 deletions(-) diff --git a/engine.go b/engine.go index 2ff8ad05..d02cbdf4 100644 --- a/engine.go +++ b/engine.go @@ -20,7 +20,7 @@ import ( ) const ( - MaxFrameSkips = 10 + MaxFrameSkips = 50 ) func NewEngine(game AnyGame) Engine { @@ -33,8 +33,6 @@ func NewEngine(game AnyGame) Engine { type Engine struct { Game AnyGame - - lastFixedUpdateAt time.Time } func (e *Engine) Run(tickrate uint, framerate uint) { @@ -69,7 +67,6 @@ func (e *Engine) Run(tickrate uint, framerate uint) { // TODO: Refactor to work without for loop for nextFixedUpdateAt.Compare(time.Now()) == -1 && loops < MaxFrameSkips { e.Game.FixedUpdate(fixedUpdDuration) - e.lastFixedUpdateAt = nextFixedUpdateAt nextFixedUpdateAt = nextFixedUpdateAt.Add(fixedUpdDuration) loops++ } @@ -78,8 +75,6 @@ func (e *Engine) Run(tickrate uint, framerate uint) { } // Render - //sinceLastFixedUpdateAt := time.Since(e.lastFixedUpdateAt) - //interpolation := float32(sinceLastFixedUpdateAt.Microseconds()) / float32(fixedUpdDuration.Microseconds()) e.Game.Render(dt) } } diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 27880733..5e8efd2d 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -28,5 +28,5 @@ func main() { game.CurrentSceneId = scenes.MainSceneId engine := gomp.NewEngine(&game) - engine.Run(50, 600) + engine.Run(20, 0) } diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index c8002ca0..84def584 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -12,6 +12,7 @@ import ( "gomp/examples/new-api/entities" "gomp/pkg/ecs" "gomp/stdcomponents" + "math/rand" ) func NewPlayerSystem() PlayerSystem { @@ -35,15 +36,21 @@ type PlayerSystem struct { } func (s *PlayerSystem) Init() { - s.Player = entities.CreatePlayer( - s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, - s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, - ) + for range 100 { + s.Player = entities.CreatePlayer( + s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, + s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, + ) - s.Controllers.Create(s.Player.Entity, components.Controller{}) + s.Player.Position.X = 100 + rand.Float32()*700 + s.Player.Position.Y = 100 + rand.Float32()*500 + } s.Player.Position.X = 100 s.Player.Position.Y = 100 + + s.Controllers.Create(s.Player.Entity, components.Controller{}) + } func (s *PlayerSystem) Run() { animationState := s.AnimationStates.Get(s.Player.Entity) diff --git a/stdsystems/animation-player.go b/stdsystems/animation-player.go index 91d41d42..c8391806 100644 --- a/stdsystems/animation-player.go +++ b/stdsystems/animation-player.go @@ -7,7 +7,6 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdsystems import ( - "fmt" "gomp/pkg/ecs" "gomp/stdcomponents" "time" @@ -32,7 +31,7 @@ func (s *AnimationPlayerSystem) Run() { s.AnimationPlayers.AllDataParallel(func(animation *stdcomponents.AnimationPlayer) bool { animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond - assert.True(animation.FrameDuration > 0, fmt.Errorf("frame duration must be greater than 0 (got %v)", animation.FrameDuration)) + assert.True(animation.FrameDuration > 0, "frame duration must be greater than 0") // Check if animation is playing backwards if animation.Speed < 0 { diff --git a/stdsystems/render.go b/stdsystems/render.go index 1c1c3e1b..baa3c74e 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -36,16 +36,19 @@ func (s *RenderSystem) Run() bool { rl.ClearBackground(rl.Black) - s.TextureRenders.AllData(func(tr *stdcomponents.TextureRender) bool { - texture := *tr.Texture - rl.DrawTexturePro(texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) - return true - }) - - s.Positions.AllData(func(position *stdcomponents.Position) bool { - rl.DrawRectangle(int32(position.X), int32(position.Y), 10, 10, rl.Red) - return true - }) + //s.TextureRenders.AllData(func(tr *stdcomponents.TextureRender) bool { + // rl.DrawTexturePro(*tr.Texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) + // return true + //}) + + for tr := range s.TextureRenders.AllData { + rl.DrawTexturePro(*tr.Texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) + } + + //s.Positions.AllData(func(position *stdcomponents.Position) bool { + // rl.DrawRectangle(int32(position.X), int32(position.Y), 10, 10, rl.Red) + // return true + //}) // rl.DrawRectangle(0, 0, 120, 120, rl.DarkGray) rl.DrawFPS(10, 10) diff --git a/stdsystems/texture-render-position.go b/stdsystems/texture-render-position.go index 76b836ab..6796d6c6 100644 --- a/stdsystems/texture-render-position.go +++ b/stdsystems/texture-render-position.go @@ -25,6 +25,8 @@ type TextureRenderPositionSystem struct { func (s *TextureRenderPositionSystem) Init() {} func (s *TextureRenderPositionSystem) Run(dt time.Duration) { + + dts := dt.Seconds() s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true @@ -35,15 +37,15 @@ func (s *TextureRenderPositionSystem) Run(dt time.Duration) { return true } - decay := 100.0 - tr.Dest.X = float32(expDecay(float64(tr.Dest.X), float64(position.X), decay, dt)) - tr.Dest.Y = float32(expDecay(float64(tr.Dest.Y), float64(position.Y), decay, dt)) + decay := 40.0 // DECAY IS TICKRATE DEPENDENT + tr.Dest.X = float32(expDecay(float64(tr.Dest.X), float64(position.X), decay, dts)) + tr.Dest.Y = float32(expDecay(float64(tr.Dest.Y), float64(position.Y), decay, dts)) return true }) } func (s *TextureRenderPositionSystem) Destroy() {} -func expDecay(a, b, decay float64, dt time.Duration) float64 { - return b + (a-b)*(math.Exp(-decay*dt.Seconds())) +func expDecay(a, b, decay, dt float64) float64 { + return b + (a-b)*(math.Exp(-decay*dt)) } From e3466982271446f680ba96b9678b608c39c154f6 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Feb 2025 16:09:01 +0300 Subject: [PATCH 019/196] fix pointers --- examples/new-api/systems/player.go | 7 ++-- go.mod | 2 +- pkg/ecs/component-manager.go | 54 ++++++++++++++++++------------ pkg/ecs/entity-manager.go | 4 +-- pkg/ecs/entity.go | 19 ++++++----- pkg/ecs/paged-array.go | 23 +++++++++++-- pkg/ecs/paged-map.go | 4 +-- stdsystems/render.go | 24 ++++--------- stdsystems/velocity.go | 16 ++++++--- 9 files changed, 90 insertions(+), 63 deletions(-) diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 84def584..582dafa3 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -36,7 +36,7 @@ type PlayerSystem struct { } func (s *PlayerSystem) Init() { - for range 100 { + for range 1 { s.Player = entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, @@ -60,7 +60,7 @@ func (s *PlayerSystem) Run() { s.Player.Velocity.X = 0 s.Player.Velocity.Y = 0 - s.Controllers.AllData(func(c *components.Controller) bool { + for range s.Controllers.EachComponent { if rl.IsKeyDown(rl.KeySpace) { *animationState = entities.PlayerStateJump } else { @@ -88,8 +88,7 @@ func (s *PlayerSystem) Run() { if rl.IsKeyPressed(rl.KeyK) { s.EntityManager.Delete(s.Player.Entity) } - return true - }) + } } func (s *PlayerSystem) Destroy() {} diff --git a/go.mod b/go.mod index 2c05bf89..2d596b51 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module gomp -go 1.23.5 +go 1.24.0 replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go v1.1.7 diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index aaac3660..bfd86f4f 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -56,11 +56,10 @@ func NewComponentManager[T any](id ComponentId) ComponentManager[T] { } type ComponentManager[T any] struct { - mx sync.Mutex - - components *PagedArray[T] - entities *PagedArray[Entity] - lookup *PagedMap[Entity, int32] + mx sync.Mutex + components PagedArray[T] + entities PagedArray[Entity] + lookup PagedMap[Entity, int32] entityManager *EntityManager entityComponentBitSet *ComponentBitSet @@ -71,9 +70,9 @@ type ComponentManager[T any] struct { // Patch TrackChanges bool // Enable TrackChanges to track changes and add them to patch - createdEntities *PagedArray[Entity] - patchedEntities *PagedArray[Entity] - deletedEntities *PagedArray[Entity] + createdEntities PagedArray[Entity] + patchedEntities PagedArray[Entity] + deletedEntities PagedArray[Entity] encoder func([]T) []byte decoder func([]byte) []T @@ -207,9 +206,9 @@ func (c *ComponentManager[T]) PatchGet() ComponentPatch { patch := ComponentPatch{ ID: c.id, - Created: c.getChangesBinary(c.createdEntities), - Patched: c.getChangesBinary(c.patchedEntities), - Deleted: c.getChangesBinary(c.deletedEntities), + Created: c.getChangesBinary(&c.createdEntities), + Patched: c.getChangesBinary(&c.patchedEntities), + Deleted: c.getChangesBinary(&c.deletedEntities), } return patch @@ -291,6 +290,28 @@ func (c *ComponentManager[T]) IsTrackingChanges() bool { // Iterators +func (c *ComponentManager[T]) EachComponent(yield func(*T) bool) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + + c.components.AllData(yield) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} + +func (c *ComponentManager[T]) EachEntity(yield func(Entity) bool) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + + c.entities.AllDataValue(yield) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} + func (c *ComponentManager[T]) All(yield func(Entity, *T) bool) { assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") @@ -328,17 +349,6 @@ func (c *ComponentManager[T]) AllParallel(yield func(Entity, *T) bool) { assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") } -func (c *ComponentManager[T]) AllData(yield func(*T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.AllData(yield) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - func (c *ComponentManager[T]) AllDataParallel(yield func(*T) bool) { assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") diff --git a/pkg/ecs/entity-manager.go b/pkg/ecs/entity-manager.go index 98a71340..6abf095a 100644 --- a/pkg/ecs/entity-manager.go +++ b/pkg/ecs/entity-manager.go @@ -44,12 +44,12 @@ import ( ) const ( - PREALLOC_DELETED_ENTITIES uint32 = 1 << 10 + PREALLOC_DEFAULT uint32 = 1 << 10 ) func NewEntityManager() EntityManager { entityManager := EntityManager{ - deletedEntityIDs: make([]Entity, 0, PREALLOC_DELETED_ENTITIES), + deletedEntityIDs: make([]Entity, 0, PREALLOC_DEFAULT), components: make(map[ComponentId]AnyComponentManagerPtr), } diff --git a/pkg/ecs/entity.go b/pkg/ecs/entity.go index 5b60f3c1..14aaa602 100644 --- a/pkg/ecs/entity.go +++ b/pkg/ecs/entity.go @@ -26,19 +26,20 @@ func (e *Entity) IsVersion(version EntityVersion) bool { } func (e *Entity) SetVersion(version EntityVersion) { - assert.True(version <= MaxEntityVersionId, "version is too high") - *e = Entity(entityType(*e) - entityType(e.GetVersion()<<(entityPower-versionPower)) | entityType(version)<<(entityPower-versionPower)) + assert.True(version <= MaxEntityGenerationId, "version is too high") + *e = Entity(entityType(*e) - entityType(e.GetVersion()<<(entityPower-generationPower)) | entityType(version)<<(entityPower-generationPower)) } func (e *Entity) GetVersion() EntityVersion { - return EntityVersion(*e >> (entityPower - versionPower)) + return EntityVersion(*e >> (entityPower - generationPower)) } const ( - entityPower = 32 - versionPower = 2 - MaxEntityVersionId EntityVersion = 1<= 0; i-- { + page = &book[i] + + for j := page.len - 1; j >= 0; j-- { + if !yield(page.data[j]) { + return + } + } + } +} + func (a *PagedArray[T]) AllDataParallel(yield func(*T) bool) { var page *ArrayPage[T] var data *[page_size]T diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 4da7977d..25342fcb 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -26,8 +26,8 @@ type MapValue[V any] struct { ok bool } -func NewPagedMap[K Entity, V any]() *PagedMap[K, V] { - return &PagedMap[K, V]{ +func NewPagedMap[K Entity, V any]() PagedMap[K, V] { + return PagedMap[K, V]{ book: make([]SlicePage[MapValue[V]], book_size), } } diff --git a/stdsystems/render.go b/stdsystems/render.go index baa3c74e..62d55d6d 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -25,36 +25,26 @@ type RenderSystem struct { func (s *RenderSystem) Init() { rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") - //currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) - //rl.SetTargetFPS(12) } func (s *RenderSystem) Run() bool { if rl.WindowShouldClose() { return false } - rl.BeginDrawing() + rl.BeginDrawing() rl.ClearBackground(rl.Black) - //s.TextureRenders.AllData(func(tr *stdcomponents.TextureRender) bool { - // rl.DrawTexturePro(*tr.Texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) - // return true - //}) - - for tr := range s.TextureRenders.AllData { + s.TextureRenders.EachComponent(func(tr *stdcomponents.TextureRender) bool { rl.DrawTexturePro(*tr.Texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) - } - - //s.Positions.AllData(func(position *stdcomponents.Position) bool { - // rl.DrawRectangle(int32(position.X), int32(position.Y), 10, 10, rl.Red) - // return true - //}) + return true + }) - // rl.DrawRectangle(0, 0, 120, 120, rl.DarkGray) + rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d", s.EntityManager.Size()), 10, 30, 20, rl.Red) + rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) rl.EndDrawing() + return true } diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 3f9aee81..54dbd10a 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -24,10 +24,18 @@ type VelocitySystem struct { func (s *VelocitySystem) Init() {} func (s *VelocitySystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - s.Velocities.AllParallel(func(entity ecs.Entity, v *stdcomponents.Velocity) bool { - position := s.Positions.Get(entity) - position.X += v.X * dtSec - position.Y += v.Y * dtSec + //s.Velocities.AllParallel(func(entity ecs.Entity, v *stdcomponents.Velocity) bool { + // position := s.Positions.Get(entity) + // position.X += v.X * dtSec + // position.Y += v.Y * dtSec + // return true + //}) + s.Velocities.EachEntity(func(e ecs.Entity) bool { + velocity := s.Velocities.Get(e) + position := s.Positions.Get(e) + + position.X += velocity.X * dtSec + position.Y += velocity.Y * dtSec return true }) } From 219d8148172961ea86f3691b302afebe3958beae Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Feb 2025 22:27:12 +0300 Subject: [PATCH 020/196] fix issues --- examples/new-api/assets/assets.go | 23 +++++++- examples/new-api/entities/player.go | 2 +- examples/new-api/systems/player.go | 2 +- go.mod | 1 + go.sum | 2 + internal/cgo/cgo-hello.go | 27 +++++++++ internal/sdl/sdl-game.go | 91 +++++++++++++++++++++++++++++ pkg/ecs/component-manager.go | 16 +++-- pkg/ecs/paged-array.go | 46 +++++++++------ pkg/ecs/paged-map.go | 12 ++-- stdsystems/render.go | 39 +++++++++++-- taskfile.yml | 6 ++ 12 files changed, 229 insertions(+), 38 deletions(-) create mode 100644 internal/cgo/cgo-hello.go create mode 100644 internal/sdl/sdl-game.go diff --git a/examples/new-api/assets/assets.go b/examples/new-api/assets/assets.go index efe64017..92b82f34 100644 --- a/examples/new-api/assets/assets.go +++ b/examples/new-api/assets/assets.go @@ -7,15 +7,36 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package assets import ( + "embed" + "fmt" rl "github.com/gen2brain/raylib-go/raylib" "github.com/negrel/assert" "gomp" + "image/png" + "log" ) +//go:embed *.png +var fs embed.FS + var Textures = gomp.CreateAssetLibrary( func(path string) rl.Texture2D { assert.True(rl.IsWindowReady(), "Window is not initialized") - return rl.LoadTexture(path) + + fmt.Print() + file, err := fs.Open(path) + if err != nil { + log.Panic("Error opening file") + } + defer file.Close() + + img, err := png.Decode(file) + if err != nil { + log.Panic("Error decoding file") + } + + rlImg := rl.NewImageFromImage(img) + return rl.LoadTextureFromImage(rlImg) }, func(path string, asset *rl.Texture2D) { assert.True(rl.IsWindowReady(), "Window is not initialized") diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index d77f69c1..88505f99 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -37,7 +37,7 @@ type Player struct { } var playerSpriteMatrix = stdcomponents.SpriteMatrix{ - Texture: assets.Textures.Get("examples/new-api/assets/milansheet.png"), + Texture: assets.Textures.Get("milansheet.png"), Origin: rl.Vector2{X: 0.5, Y: 0.5}, FPS: 12, Animations: []stdcomponents.SpriteMatrixAnimation{ diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 582dafa3..7204ba4d 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -36,7 +36,7 @@ type PlayerSystem struct { } func (s *PlayerSystem) Init() { - for range 1 { + for range 100_000 { s.Player = entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, diff --git a/go.mod b/go.mod index 2d596b51..298e69cd 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/veandco/go-sdl2 v0.4.40 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect golang.org/x/mod v0.21.0 // indirect diff --git a/go.sum b/go.sum index 4dff1431..adafe435 100644 --- a/go.sum +++ b/go.sum @@ -171,6 +171,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/U= +github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/yohamta/donburi v1.15.7 h1:so/vHf1L133d0SFVrCUzMMueh2ko39wRkrcpNLdzvz8= diff --git a/internal/cgo/cgo-hello.go b/internal/cgo/cgo-hello.go new file mode 100644 index 00000000..37b9bbe8 --- /dev/null +++ b/internal/cgo/cgo-hello.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +/* +double add(double a, double b) { + return a + b; +} +*/ +import "C" +import "fmt" + +func main() { + fmt.Println(C.add(1, 2)) +} diff --git a/internal/sdl/sdl-game.go b/internal/sdl/sdl-game.go new file mode 100644 index 00000000..37945fff --- /dev/null +++ b/internal/sdl/sdl-game.go @@ -0,0 +1,91 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "fmt" + "github.com/veandco/go-sdl2/img" + "github.com/veandco/go-sdl2/sdl" + "os" +) + +var winTitle string = "Go-SDL2 Texture" +var winWidth, winHeight int32 = 800, 600 +var imageName string = "../../assets/test.png" + +func run() int { + var window *sdl.Window + var renderer *sdl.Renderer + var texture *sdl.Texture + var src, dst sdl.Rect + var err error + + window, err = sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, + winWidth, winHeight, sdl.WINDOW_SHOWN) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err) + return 1 + } + defer window.Destroy() + + renderer, err = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", err) + return 2 + } + defer renderer.Destroy() + + image, err := img.Load(imageName) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to load PNG: %s\n", err) + return 3 + } + defer image.Free() + + texture, err = renderer.CreateTextureFromSurface(image) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create texture: %s\n", err) + return 4 + } + defer texture.Destroy() + + src = sdl.Rect{0, 0, 512, 512} + dst = sdl.Rect{100, 50, 512, 512} + + running := true + + for running { + for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { + switch event.(type) { + + case *sdl.QuitEvent: + running = false + } + } + + renderer.Clear() + renderer.SetDrawColor(255, 0, 0, 255) + renderer.FillRect(&sdl.Rect{0, 0, int32(winWidth), int32(winHeight)}) + renderer.Copy(texture, &src, &dst) + renderer.Present() + sdl.Delay(16) + } + + return 0 +} + +func main() { + os.Exit(run()) +} diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index bfd86f4f..4a89459d 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -41,7 +41,7 @@ func NewComponentManager[T any](id ComponentId) ComponentManager[T] { newManager := ComponentManager[T]{ components: NewPagedArray[T](), entities: NewPagedArray[Entity](), - lookup: NewPagedMap[Entity, int32](), + lookup: NewPagedMap[Entity, int](), id: id, isInitialized: true, @@ -59,7 +59,7 @@ type ComponentManager[T any] struct { mx sync.Mutex components PagedArray[T] entities PagedArray[Entity] - lookup PagedMap[Entity, int32] + lookup PagedMap[Entity, int] entityManager *EntityManager entityComponentBitSet *ComponentBitSet @@ -80,7 +80,7 @@ type ComponentManager[T any] struct { // ComponentChanges with byte encoded Components type ComponentChanges struct { - Len int32 + Len int Components []byte Entities []Entity } @@ -318,7 +318,7 @@ func (c *ComponentManager[T]) All(yield func(Entity, *T) bool) { assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - c.components.All(func(i int32, d *T) bool { + c.components.All(func(i int, d *T) bool { assert.True(d != nil) entity := c.entities.Get(i) assert.True(entity != nil) @@ -337,7 +337,7 @@ func (c *ComponentManager[T]) AllParallel(yield func(Entity, *T) bool) { assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - c.components.AllParallel(func(i int32, t *T) bool { + c.components.AllParallel(func(i int, t *T) bool { entity := c.entities.Get(i) assert.True(entity != nil) entId := *entity @@ -362,7 +362,11 @@ func (c *ComponentManager[T]) AllDataParallel(yield func(*T) bool) { // Utils -func (c *ComponentManager[T]) Len() int32 { +func (c *ComponentManager[T]) RawComponents(ptr []T) { + c.components.Raw(ptr) +} + +func (c *ComponentManager[T]) Len() int { assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") return c.components.Len() diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 908ca7a5..86975f70 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -15,8 +15,8 @@ import ( type PagedArray[T any] struct { book []ArrayPage[T] - currentPageIndex int32 - len int32 + currentPageIndex int + len int parallelCount uint8 } @@ -27,11 +27,11 @@ func NewPagedArray[T any]() (a PagedArray[T]) { return a } -func (a *PagedArray[T]) Len() int32 { +func (a *PagedArray[T]) Len() int { return a.len } -func (a *PagedArray[T]) Get(index int32) *T { +func (a *PagedArray[T]) Get(index int) *T { assert.True(index >= 0, "index out of range") assert.True(index < a.len, "index out of range") @@ -41,7 +41,7 @@ func (a *PagedArray[T]) Get(index int32) *T { return &(page.data[index]) } -func (a *PagedArray[T]) Set(index int32, value T) *T { +func (a *PagedArray[T]) Set(index int, value T) *T { assert.True(index >= 0, "index out of range") assert.True(index < a.len, "index out of range") @@ -54,7 +54,7 @@ func (a *PagedArray[T]) Set(index int32, value T) *T { } func (a *PagedArray[T]) Append(value T) *T { - if a.currentPageIndex >= int32(len(a.book)) { + if a.currentPageIndex >= len(a.book) { newBooks := make([]ArrayPage[T], len(a.book)*2) a.book = append(a.book, newBooks...) } @@ -95,7 +95,7 @@ func (a *PagedArray[T]) Reset() { a.len = 0 } -func (a *PagedArray[T]) Copy(fromIndex, toIndex int32) { +func (a *PagedArray[T]) Copy(fromIndex, toIndex int) { assert.True(fromIndex >= 0, "index out of range") assert.True(fromIndex < a.len, "index out of range") from := a.Get(fromIndex) @@ -107,7 +107,7 @@ func (a *PagedArray[T]) Copy(fromIndex, toIndex int32) { *to = *from } -func (a *PagedArray[T]) Swap(i, j int32) (newI, NewJ *T) { +func (a *PagedArray[T]) Swap(i, j int) (newI, NewJ *T) { assert.True(i >= 0, "index out of range") assert.True(i < a.len, "index out of range") x := a.Get(i) @@ -127,9 +127,17 @@ func (a *PagedArray[T]) Last() *T { return a.Get(index) } -func (a *PagedArray[T]) getPageIdAndIndex(index int32) (int32, int32) { +func (a *PagedArray[T]) Raw(result []T) { + result = result[:0] + for i := 0; i <= a.currentPageIndex; i++ { + page := &a.book[i] + result = append(result[:i*1024], append(result[i*1024:], page.data[:page.len]...)...) + } +} + +func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { pageId := index >> page_size_shift - assert.True(pageId < int32(len(a.book)), "index out of range") + assert.True(pageId < len(a.book), "index out of range") index %= page_size assert.True(index < page_size, "index out of range") @@ -137,9 +145,9 @@ func (a *PagedArray[T]) getPageIdAndIndex(index int32) (int32, int32) { return pageId, index } -func (a *PagedArray[T]) All(yield func(int32, *T) bool) { +func (a *PagedArray[T]) All(yield func(int, *T) bool) { var page *ArrayPage[T] - var index_offset int32 + var index_offset int book := a.book @@ -159,16 +167,16 @@ func (a *PagedArray[T]) All(yield func(int32, *T) bool) { } } -func (a *PagedArray[T]) AllParallel(yield func(int32, *T) bool) { +func (a *PagedArray[T]) AllParallel(yield func(int, *T) bool) { var page *ArrayPage[T] var data *[page_size]T - var index_offset int32 + var index_offset int book := a.book wg := new(sync.WaitGroup) gorutineBudget := a.parallelCount - runner := func(data *[page_size]T, offset int32, startIndex int32, wg *sync.WaitGroup) { + runner := func(data *[page_size]T, offset int, startIndex int, wg *sync.WaitGroup) { defer wg.Done() for j := startIndex; j >= 0; j-- { if !yield(offset+j, &(data[j])) { @@ -185,7 +193,7 @@ func (a *PagedArray[T]) AllParallel(yield func(int32, *T) bool) { for i := a.currentPageIndex; i >= 0; i-- { page = &book[i] data = &page.data - index_offset = int32(i) << page_size_shift + index_offset = int(i) << page_size_shift if gorutineBudget > 0 { go runner(data, index_offset, page.len-1, wg) @@ -246,7 +254,7 @@ func (a *PagedArray[T]) AllDataParallel(yield func(*T) bool) { book := a.book wg := new(sync.WaitGroup) gorutineBudget := a.parallelCount - runner := func(data *[page_size]T, startIndex int32, wg *sync.WaitGroup) { + runner := func(data *[page_size]T, startIndex int, wg *sync.WaitGroup) { defer wg.Done() for j := startIndex; j >= 0; j-- { if !yield(&(data[j])) { @@ -276,11 +284,11 @@ func (a *PagedArray[T]) AllDataParallel(yield func(*T) bool) { } type SlicePage[T any] struct { - len int32 + len int data []T } type ArrayPage[T any] struct { - len int32 + len int data [page_size]T } diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 25342fcb..8eb89302 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -11,14 +11,14 @@ import ( ) const ( - page_size_shift int32 = 10 - page_size int32 = 1 << page_size_shift - book_size int32 = 1 << 10 + page_size_shift int = 14 + page_size int = 1 << page_size_shift + book_size int = 1 << 10 ) type MapPage[K Entity, V any] map[K]V type PagedMap[K Entity, V any] struct { - len int32 + len int book []SlicePage[MapValue[V]] } type MapValue[V any] struct { @@ -83,10 +83,10 @@ func (m *PagedMap[K, V]) Delete(key K) { func (m *PagedMap[K, V]) getPageIdAndIndex(key K) (page_id int, index int) { page_id = int(key) >> page_size_shift - index = int(int32(key) % page_size) + index = int(key) % page_size return } -func (m *PagedMap[K, V]) Len() int32 { +func (m *PagedMap[K, V]) Len() int { return m.len } diff --git a/stdsystems/render.go b/stdsystems/render.go index 62d55d6d..9c9e64c6 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -13,6 +13,10 @@ import ( "gomp/stdcomponents" ) +const ( + batchSize = 1 << 13 // Maximum batch size supported by Raylib +) + func NewRenderSystem() RenderSystem { return RenderSystem{} } @@ -21,23 +25,50 @@ type RenderSystem struct { EntityManager *ecs.EntityManager TextureRenders *stdcomponents.TextureRenderComponentManager Positions *stdcomponents.PositionComponentManager + camera rl.Camera2D + trBatch []stdcomponents.TextureRender } func (s *RenderSystem) Init() { rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") + //InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") + + s.camera = rl.Camera2D{ + Target: rl.NewVector2(0, 0), + Offset: rl.NewVector2(0, 0), + Rotation: 0, + Zoom: 1, + } } + func (s *RenderSystem) Run() bool { if rl.WindowShouldClose() { return false } + textureLen := s.TextureRenders.Len() + if len(s.trBatch) < textureLen { + s.trBatch = make([]stdcomponents.TextureRender, textureLen) + } + s.TextureRenders.RawComponents(s.trBatch) + rl.BeginDrawing() rl.ClearBackground(rl.Black) - s.TextureRenders.EachComponent(func(tr *stdcomponents.TextureRender) bool { - rl.DrawTexturePro(*tr.Texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) - return true - }) + for batch := 0; batch < textureLen; batch += batchSize { + endBatch := batch + batchSize + if endBatch > textureLen { + endBatch = textureLen + } + + rl.BeginMode2D(s.camera) + for i := batch; i < endBatch; i++ { + tr := &s.trBatch[i] + txt := *tr.Texture + rl.DrawTexturePro(txt, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) + } + rl.EndMode2D() + } rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) rl.DrawFPS(10, 10) diff --git a/taskfile.yml b/taskfile.yml index ceed8639..0b8a5ecd 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -37,3 +37,9 @@ tasks: pprof-cpu: cmds: - go tool pprof -http=":8000" ./cpu.out + + build: + env: + CGO_ENABLED: 0 + cmds: + - go build examples/new-api/game.go From 35a958fb64cf50501c462997f533bc7d4fe3ee4b Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 26 Feb 2025 11:05:02 +0300 Subject: [PATCH 021/196] feat dist build tasks --- taskfile.yml | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/taskfile.yml b/taskfile.yml index 0b8a5ecd..2871a146 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -38,8 +38,42 @@ tasks: cmds: - go tool pprof -http=":8000" ./cpu.out - build: + build-win64: env: - CGO_ENABLED: 0 + CGO_ENABLED: 1 cmds: - - go build examples/new-api/game.go + - go build examples/new-api/game.go -o ./.dist/game-win64.exe + + build-mac: + - task: build-darwin-amd64 + - task: build-darwin-arm64 + - task: build-darwin-universal + - task: sign-darwin-universal + + build-darwin-amd64: + env: + CGO_ENABLED: 1 + cmds: + - env CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -o ./.dist/game-darwin-amd64 examples/new-api/game.go + - chmod +x ./.dist/game-darwin-amd64 + + build-darwin-arm64: + env: + CGO_ENABLED: 1 + cmds: + - env CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -o ./.dist/game-darwin-arm64 examples/new-api/game.go + - chmod +x ./.dist/game-darwin-arm64 + + build-darwin-universal: + cmds: + - lipo -create -output ./.dist/game-universal ./.dist/game-darwin-amd64 ./.dist/game-darwin-arm64 + - chmod +x ./.dist/game-universal + + sign-darwin-universal: + cmds: + - mkdir -p ./.dist/game.app/Contents/MacOS + - mv ./.dist/game-universal ./.dist/game.app/Contents/MacOS/game + - chmod +x ./.dist/game.app + - codesign --timestamp --options=runtime --deep -fs milanrodd-cert -v ./.dist/game.app + - xattr -cr ./.dist/game.app + - /usr/bin/ditto -c -k --keepParent ./.dist/game.app ./.dist/game.zip From 05bad1d30cdd4186f474b1b96ac28fd3a28a4f36 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 26 Feb 2025 12:46:50 +0300 Subject: [PATCH 022/196] feat mem pprof --- stdsystems/debug.go | 14 ++++++++++++++ taskfile.yml | 14 +++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/stdsystems/debug.go b/stdsystems/debug.go index 026ff089..d9c62e28 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -28,11 +28,25 @@ func (s *DebugSystem) Run() { if s.pprofEnabled { pprof.StopCPUProfile() fmt.Println("CPU Profile Stopped") + + // Create a memory profile file + memProfileFile, err := os.Create("mem.out") + if err != nil { + panic(err) + } + defer memProfileFile.Close() + + // Write memory profile to file + if err := pprof.WriteHeapProfile(memProfileFile); err != nil { + panic(err) + } + fmt.Println("Memory profile written to mem.prof") } else { f, err := os.Create("cpu.out") if err != nil { log.Fatal(err) } + err = pprof.StartCPUProfile(f) if err != nil { log.Fatal(err) diff --git a/taskfile.yml b/taskfile.yml index 2871a146..068ec4d3 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -30,19 +30,23 @@ tasks: cmds: - protoc --go_out=. internal/**/*.proto - pprof: - cmds: - - go tool pprof -http=":8000" 'http://localhost:3000/debug/pprof/profile?seconds=10' - pprof-cpu: cmds: - go tool pprof -http=":8000" ./cpu.out + pprof-mem: + cmds: + - go tool pprof -http=":8001" ./mem.out + + pprof-trace: + cmds: + - go tool trace trace.out + build-win64: env: CGO_ENABLED: 1 cmds: - - go build examples/new-api/game.go -o ./.dist/game-win64.exe + - go build -o ./.dist/game-win64.exe examples/new-api/game.go build-mac: - task: build-darwin-amd64 From e1387593da1c52789353844b6ddf8e8da5ab3e3f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 26 Feb 2025 13:06:59 +0300 Subject: [PATCH 023/196] fix huge amount of memory allocation --- examples/new-api/components/ids.go | 7 ++++--- pkg/ecs/paged-map.go | 2 +- stdcomponents/ids.go | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go index 0fc99884..774ca3a2 100644 --- a/examples/new-api/components/ids.go +++ b/examples/new-api/components/ids.go @@ -14,10 +14,11 @@ Thank you for your support! package components -import "gomp/pkg/ecs" +import ( + "gomp/stdcomponents" +) const ( - InvalidComponentId ecs.ComponentId = iota - HealthComponentId + HealthComponentId = iota + stdcomponents.StdComponentIds ControllerComponentId ) diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 8eb89302..42e294f8 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -13,7 +13,7 @@ import ( const ( page_size_shift int = 14 page_size int = 1 << page_size_shift - book_size int = 1 << 10 + book_size int = 1 << 4 ) type MapPage[K Entity, V any] map[K]V diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index ecfd90ab..448ae074 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -11,7 +11,7 @@ import ( ) const ( - InvalidComponentId ecs.ComponentId = 1<<16 - 1 - iota + InvalidComponentId ecs.ComponentId = iota PositionComponentId RotationComponentId ScaleComponentId @@ -26,4 +26,5 @@ const ( AnimationStateComponentId TintComponentId NetworkComponentId + StdComponentIds ) From 9e8e8e44f0e15003d57bf2ad93c4c140fd6cf990 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 27 Feb 2025 20:21:11 +0300 Subject: [PATCH 024/196] sandbox sdl and c --- aos_soa_test.go | 181 +++++++++++++++++ assets/test.ttf | Bin 0 -> 756072 bytes go.mod | 3 +- go.sum | 4 + internal/sdl/sdl-game.go | 91 --------- internal/sdl2/sdl-game.go | 107 ++++++++++ internal/sdl3/sdl-game.go | 82 ++++++++ pkg/ecs/grid.go | 162 ++++++++++++++++ pkg/ecs/lookup-bench_test.go | 365 +++++++++++++++++++++++++++++++++++ pkg/ecs/lookup.go | 83 ++++++++ pkg/ecs/lookup_test.go | 78 ++++++++ taskfile.yml | 2 + 12 files changed, 1066 insertions(+), 92 deletions(-) create mode 100644 aos_soa_test.go create mode 100644 assets/test.ttf delete mode 100644 internal/sdl/sdl-game.go create mode 100644 internal/sdl2/sdl-game.go create mode 100644 internal/sdl3/sdl-game.go create mode 100644 pkg/ecs/grid.go create mode 100644 pkg/ecs/lookup-bench_test.go create mode 100644 pkg/ecs/lookup.go create mode 100644 pkg/ecs/lookup_test.go diff --git a/aos_soa_test.go b/aos_soa_test.go new file mode 100644 index 00000000..756e9568 --- /dev/null +++ b/aos_soa_test.go @@ -0,0 +1,181 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import ( + "math/rand" + "testing" +) + +type Float3 struct { + X, Y, Z float64 +} + +// Array of Structures (AoS) +type Unit struct { + Pos Float3 + Velocity Float3 + HP int + Offset [AdditionalDataPer64]int64 +} + +type UnitsAoS struct { + Units []Unit +} + +// Structure of Arrays (SoA) +type UnitsSoA struct { + Positions [][pageSize]Float3 + Velocities [][pageSize]Float3 + HPs [][pageSize]int + Offsets [][pageSize][AdditionalDataPer64]int64 +} + +func UpdatePosition(pos *Float3, velocity *Float3) { + pos.X += velocity.X + pos.Y += velocity.Y + pos.Z += velocity.Z +} + +func TakeDamage(hp *int, damage int) { + *hp = max(0, *hp-damage) +} + +func UpdateOffset(offset *[AdditionalDataPer64]int64) { + for i := range offset { + offset[i]++ + } +} + +func ShouldRender(pos *Float3) bool { + return pos.X > 10.0 && pos.Y < 5.0 && pos.Z > 0.0 +} + +func GenerateUnitsAoS(count int) UnitsAoS { + units := UnitsAoS{Units: make([]Unit, count)} + for i := 0; i < count; i++ { + units.Units[i] = Unit{ + Pos: Float3{ + X: rand.Float64()*40 - 20, + Y: rand.Float64()*40 - 20, + Z: rand.Float64()*40 - 20, + }, + Velocity: Float3{ + X: rand.Float64()*2 - 1, + Y: rand.Float64()*2 - 1, + Z: rand.Float64()*2 - 1, + }, + HP: rand.Intn(151) + 50, + } + } + return units +} + +func MakeUnitsSoA(aos UnitsAoS) UnitsSoA { + soa := UnitsSoA{ + Positions: make([][pageSize]Float3, len(aos.Units)/pageSize+1), + Velocities: make([][pageSize]Float3, len(aos.Units)/pageSize+1), + HPs: make([][pageSize]int, len(aos.Units)/pageSize+1), + Offsets: make([][pageSize][AdditionalDataPer64]int64, len(aos.Units)/pageSize+1), + } + + for i, unit := range aos.Units { + a, b := i>>pageShift, i%pageSize + soa.Positions[a][b] = unit.Pos + soa.Velocities[a][b] = unit.Velocity + soa.HPs[a][b] = unit.HP + soa.Offsets[a][b] = unit.Offset + } + return soa +} + +func PositionSystem(positions [][pageSize]Float3, velocities [][pageSize]Float3) { + for i := 0; i < len(positions); i++ { + for j := 0; j < len(positions[i]); j++ { + UpdatePosition(&positions[i][j], &velocities[i][j]) + } + } +} + +func TakeDamageSystem(hps [][pageSize]int) { + for i := 0; i < len(hps); i++ { + for j := 0; j < len(hps[i]); j++ { + TakeDamage(&hps[i][j], 33) + } + } +} + +func OffsetSystem(offsets [][pageSize][AdditionalDataPer64]int64) { + for i := 0; i < len(offsets); i++ { + for j := 0; j < len(offsets[i]); j++ { + UpdateOffset(&offsets[i][j]) + } + } +} + +func RenderSystem(positions [][pageSize]Float3, unitsToRender []int) { + for i := 0; i < len(positions); i++ { + for j := 0; j < len(positions[i]); j++ { + if ShouldRender(&positions[i][j]) { + index := j + i*len(positions[i]) + unitsToRender = append(unitsToRender, index) + } + } + } +} + +// RuDimbo Team +func BenchmarkUpdateRuDimbo(b *testing.B) { + b.ReportAllocs() + + units := GenerateUnitsAoS(NumOfEntities) + unitsToRender := make([]int, 0, NumOfEntities) + + for b.Loop() { + for i := range units.Units { + unit := &units.Units[i] + UpdatePosition(&unit.Pos, &unit.Velocity) + if ShouldRender(&unit.Pos) { + unitsToRender = append(unitsToRender, i) + } + TakeDamage(&unit.HP, 33) + UpdateOffset(&unit.Offset) + } + unitsToRender = unitsToRender[:0] + } +} + +// Rodd Team +func BenchmarkUpdateRodd(b *testing.B) { + b.ReportAllocs() + soaUnits := MakeUnitsSoA(GenerateUnitsAoS(NumOfEntities)) + unitsToRender := make([]int, 0, NumOfEntities) + for b.Loop() { + positions := soaUnits.Positions + velocities := soaUnits.Velocities + hps := soaUnits.HPs + offsets := soaUnits.Offsets + PositionSystem(positions, velocities) + TakeDamageSystem(hps) + OffsetSystem(offsets) + RenderSystem(positions, unitsToRender) + unitsToRender = unitsToRender[:0] + } +} + +const NumOfEntities = 10_000_000 +const AdditionalDataPer64 = 30 +const pageShift = 10 +const pageSize = 1 << pageShift diff --git a/assets/test.ttf b/assets/test.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5267218852f631928716a8005d7bf4b492aaf83d GIT binary patch literal 756072 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≫tIz1 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 z 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

RzTs%1vLC_XPFIJ7%Ltna5(?OiGWe0J# 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

<2oeM7=vk_bEXQ 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 z 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 R#=GS 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%0AMHb&6)ucA$?oY@-r;RL&-VEKC3<@u^6xuB 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
  • >p_9aj3F<#rd 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%0Dfs)Y}26I#p*Un-U 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�GuUeB^t8W5uO2i(Q0o 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

    fCLbdK}l0(LZ#yJB4tcH<-kiya**=oZzY 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 VqFKN^vu_wr^apPpG44JU{68;SYeN74 literal 0 HcmV?d00001 diff --git a/examples/new-api/assets/gun_sound.wav b/examples/new-api/assets/gun_sound.wav new file mode 100644 index 0000000000000000000000000000000000000000..79cbb24c9f10aeb8fe507d27d9abecf2cfb974b2 GIT binary patch literal 396944 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%0AB%~f5_ZuNk<_lJ*Gr- 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 literal 0 HcmV?d00001 diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index 7d95ce39..acfaaeae 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -34,7 +34,7 @@ func NewSystemList() SystemList { TextureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(), Sprite: stdsystems.NewSpriteSystem(), SpriteMatrix: stdsystems.NewSpriteMatrixSystem(), - AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{&assets.Textures}), + AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{&assets.Textures, &assets.Audio}), YSort: stdsystems.NewYSortSystem(), CollisionDetectionGrid: stdsystems.NewCollisionDetectionGridSystem(), CollisionDetectionBVH: stdsystems.NewCollisionDetectionBVHSystem(), @@ -44,6 +44,8 @@ func NewSystemList() SystemList { RenderAssterodd: systems.NewRenderAssteroddSystem(), RenderBogdan: systems.NewRenderBogdanSystem(), + Audio: systems.NewAudioSystem(), + AssteroddSystem: systems.NewAssteroddSystem(), CollisionHandler: systems.NewCollisionHandlerSystem(), SpaceshipIntents: systems.NewSpaceshipIntentsSystem(), @@ -76,6 +78,8 @@ type SystemList struct { RenderAssterodd systems.RenderAssteroddSystem RenderBogdan systems.RenderBogdanSystem + Audio systems.AudioSystem + AssteroddSystem systems.AssteroddSystem CollisionHandler systems.CollisionHandlerSystem SpaceshipIntents systems.SpaceshipIntentsSystem diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index a02da67b..2cb15e31 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -96,6 +96,7 @@ func (s *AssteroddScene) Init() { s.World.Systems.RenderAssterodd.Init() s.World.Systems.Debug.Init() s.World.Systems.AssetLib.Init() + s.World.Systems.Audio.Init() } func (s *AssteroddScene) Update(dt time.Duration) gomp.SceneId { @@ -157,6 +158,7 @@ func (s *AssteroddScene) Destroy() { s.World.Systems.Debug.Destroy() s.World.Systems.AssetLib.Destroy() s.World.Systems.RenderAssterodd.Destroy() + s.World.Systems.Audio.Destroy() } func (s *AssteroddScene) OnEnter() { diff --git a/examples/new-api/systems/audio.go b/examples/new-api/systems/audio.go new file mode 100644 index 00000000..78016051 --- /dev/null +++ b/examples/new-api/systems/audio.go @@ -0,0 +1,45 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package systems + +import ( + "gomp/examples/new-api/assets" + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "time" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +func NewAudioSystem() AudioSystem { + return AudioSystem{} +} + +type AudioSystem struct { + EntityManager *ecs.EntityManager + SpaceshipIntents *components.SpaceshipIntentComponentManager +} + +func (s *AudioSystem) Init() { + rl.InitAudioDevice() + + assets.Audio.Load("damage_sound.wav") + assets.Audio.Load("fly_sound.wav") + assets.Audio.Load("gun_sound.wav") +} +func (s *AudioSystem) Run(dt time.Duration) {} +func (s *AudioSystem) Destroy() { + rl.CloseAudioDevice() +} diff --git a/examples/new-api/systems/collision-handler.go b/examples/new-api/systems/collision-handler.go index 28e71d08..4a2d73ea 100644 --- a/examples/new-api/systems/collision-handler.go +++ b/examples/new-api/systems/collision-handler.go @@ -15,6 +15,8 @@ Thank you for your support! package systems import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/assets" "gomp/examples/new-api/components" "gomp/pkg/ecs" "gomp/stdcomponents" @@ -98,7 +100,12 @@ func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bo asteroidTag := s.AsteroidTags.Get(e2) if asteroidTag != nil { hp := s.Hps.Get(e1) + damageSound := assets.Audio.Get("damage_sound.wav") + rl.SetSoundPitch(*damageSound, float32(1.0+(float32(hp.MaxHp)-float32(hp.Hp))/float32(hp.MaxHp))) + hp.Hp -= 1 + + rl.PlaySound(*damageSound) return true } diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index 2d916e38..39d1b655 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -15,6 +15,7 @@ Thank you for your support! package systems import ( + "gomp/examples/new-api/assets" "gomp/examples/new-api/components" "gomp/examples/new-api/entities" "gomp/pkg/ecs" @@ -22,6 +23,8 @@ import ( "gomp/vectors" "math" "time" + + rl "github.com/gen2brain/raylib-go/raylib" ) func NewSpaceshipIntentsSystem() SpaceshipIntentsSystem { @@ -61,6 +64,7 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { rot := s.Rotations.Get(entity) pos := s.Positions.Get(entity) weapon := s.Weapons.Get(entity) + hp := s.Hps.Get(entity) if intent.RotateLeft { rot.Angle -= rotateSpeed * vectors.Radians(dtSec) @@ -89,6 +93,19 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { } } + flySound := assets.Audio.Get("fly_sound.wav") + flySoundIsPlaying := rl.IsSoundPlaying(*flySound) + + absMoveSpeed := math.Abs(float64(s.moveSpeed)) + + rl.SetSoundVolume(*flySound, float32(absMoveSpeed/float64(moveSpeedMax))) + + if (intent.MoveUp || intent.MoveDown || intent.RotateLeft || intent.RotateRight || s.moveSpeed != 0) && !flySoundIsPlaying { + rl.PlaySound(*flySound) + } else if !(intent.MoveUp || intent.MoveDown || intent.RotateLeft || intent.RotateRight || s.moveSpeed != 0) && flySoundIsPlaying || hp.Hp == 0 { + rl.StopSound(*flySound) + } + vel.Y = float32(math.Cos(rot.Angle+math.Pi)) * s.moveSpeed vel.X = -float32(math.Sin(rot.Angle+math.Pi)) * s.moveSpeed @@ -113,6 +130,9 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { }, pos.XY.X, pos.XY.Y, angle, bulletVelocityX, bulletVelocityY) } weapon.CooldownLeft = weapon.Cooldown + fireSoundAsset := assets.Audio.Get("gun_sound.wav") + fireSound := rl.LoadSoundAlias(*fireSoundAsset) + rl.PlaySound(fireSound) } } else { weapon.CooldownLeft -= dt From 96aae0b40fa35d8e6968e77c3bab07a23c338e68 Mon Sep 17 00:00:00 2001 From: bitver Date: Wed, 26 Mar 2025 17:55:44 +0500 Subject: [PATCH 071/196] rigid body mass fun --- examples/new-api/entities/asteroid.go | 2 +- examples/new-api/entities/satellite.go | 7 +++++ examples/new-api/entities/spaceship.go | 2 +- examples/new-api/systems/asterodd.go | 1 + examples/new-api/systems/spaceship-intents.go | 30 +++++++++---------- stdcomponents/ids.go | 1 + stdcomponents/velocity.go | 14 ++++++++- stdsystems/collision-reslution.go | 30 ++++++++++++++++++- stdsystems/velocity.go | 15 ++++++++-- 9 files changed, 81 insertions(+), 21 deletions(-) diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index 64f5b159..f5fbf10b 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -100,7 +100,7 @@ func CreateAsteroid( }) props.RigidBodies.Create(bullet, stdcomponents.RigidBody{ IsStatic: true, - Mass: 1, + Mass: 10, }) return bullet diff --git a/examples/new-api/entities/satellite.go b/examples/new-api/entities/satellite.go index 6f9cd774..646f0b1b 100644 --- a/examples/new-api/entities/satellite.go +++ b/examples/new-api/entities/satellite.go @@ -32,6 +32,7 @@ type CreateSatelliteManagers struct { Sprites *stdcomponents.SpriteComponentManager BoxColliders *stdcomponents.BoxColliderComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager + Velocities *stdcomponents.VelocityComponentManager } func CreateSatellite( @@ -56,6 +57,11 @@ func CreateSatellite( }, }) + props.Velocities.Create(satellite, stdcomponents.Velocity{ + X: 0, + Y: 0, + }) + props.Sprites.Create(satellite, stdcomponents.Sprite{ Texture: assets.Textures.Get("satellite_B.png"), Origin: rl.Vector2{X: 32, Y: 40}, @@ -77,6 +83,7 @@ func CreateSatellite( }) props.RigidBodies.Create(satellite, stdcomponents.RigidBody{ IsStatic: false, + Mass: 1, }) return satellite diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go index f840b26e..6f65499b 100644 --- a/examples/new-api/entities/spaceship.go +++ b/examples/new-api/entities/spaceship.go @@ -91,7 +91,7 @@ func CreateSpaceShip( props.RigidBodies.Create(spaceShip, stdcomponents.RigidBody{ IsStatic: false, - Mass: 1, + Mass: 2, }) props.PlayerTags.Create(spaceShip, components.PlayerTag{}) diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index ffb7beed..c53fb315 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -69,6 +69,7 @@ func (s *AssteroddSystem) Init() { Positions: s.Positions, Rotations: s.Rotations, Scales: s.Scales, + Velocities: s.Velocities, Sprites: s.Sprites, BoxColliders: s.BoxColliders, RigidBodies: s.RigidBodies, diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index 336b9a95..e5f66ac6 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -69,33 +69,34 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { rot.Angle += rotateSpeed * vectors.Radians(dtSec) } if intent.MoveUp { - s.moveSpeed += speedIncrement - if s.moveSpeed > moveSpeedMax { - s.moveSpeed = moveSpeedMax + vel.Y += float32(math.Cos(rot.Angle+math.Pi)) * speedIncrement + vel.X -= float32(math.Sin(rot.Angle+math.Pi)) * speedIncrement + if vel.Vec2().Length() > moveSpeedMax { + vel.SetVec2(vel.Vec2().Normalize().Scale(moveSpeedMax)) } } if intent.MoveDown { - s.moveSpeed -= speedIncrement - if s.moveSpeed < moveSpeedMaxBackwards { - s.moveSpeed = moveSpeedMaxBackwards + vel.Y -= float32(math.Cos(rot.Angle+math.Pi)) * speedIncrement + vel.X += float32(math.Sin(rot.Angle+math.Pi)) * speedIncrement + if vel.Vec2().Length() < moveSpeedMaxBackwards { + vel.SetVec2(vel.Vec2().Normalize().Scale(moveSpeedMaxBackwards)) } } if !intent.MoveUp && !intent.MoveDown { - if s.moveSpeed > 0 { - s.moveSpeed -= speedIncrement - } else if s.moveSpeed < 0 { - s.moveSpeed += speedIncrement + if vel.Vec2().Length() > 0 { + deceleration := vel.Vec2().Normalize().Scale(speedIncrement) + vel.SetVec2(vel.Vec2().Sub(deceleration)) + if vel.Vec2().Length() < speedIncrement { + vel.SetVec2(vectors.Vec2{0, 0}) + } } } - vel.Y = float32(math.Cos(rot.Angle+math.Pi)) * s.moveSpeed - vel.X = -float32(math.Sin(rot.Angle+math.Pi)) * s.moveSpeed - if weapon.CooldownLeft <= 0 { if intent.Fire { var count int = 360 - for i := range count { + for i := 0; i < count; i++ { var angle = math.Pi*2/float64(count)*float64(i) + rot.Angle - math.Pi/2 bulletVelocityY := vel.Y + float32(math.Cos(angle+math.Pi))*bulletSpeed @@ -121,5 +122,4 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { return true }) } - func (s *SpaceshipIntentsSystem) Destroy() {} diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 063e8bd5..2d5f67c3 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -37,5 +37,6 @@ const ( SpatialIndexComponentId AABBComponentId RigidBodyComponentId + MassComponentId StdComponentIds ) diff --git a/stdcomponents/velocity.go b/stdcomponents/velocity.go index 059c8e00..24098f75 100644 --- a/stdcomponents/velocity.go +++ b/stdcomponents/velocity.go @@ -14,12 +14,24 @@ Thank you for your support! package stdcomponents -import "gomp/pkg/ecs" +import ( + "gomp/pkg/ecs" + "gomp/vectors" +) type Velocity struct { X, Y float32 } +func (v Velocity) Vec2() vectors.Vec2 { + return vectors.Vec2{X: v.X, Y: v.Y} +} + +func (v *Velocity) SetVec2(velocity vectors.Vec2) { + v.X = velocity.X + v.Y = velocity.Y +} + type VelocityComponentManager = ecs.ComponentManager[Velocity] func NewVelocityComponentManager() VelocityComponentManager { diff --git a/stdsystems/collision-reslution.go b/stdsystems/collision-reslution.go index f1c22b7c..2e10fb8e 100644 --- a/stdsystems/collision-reslution.go +++ b/stdsystems/collision-reslution.go @@ -30,6 +30,7 @@ type CollisionResolutionSystem struct { Collisions *stdcomponents.CollisionComponentManager Positions *stdcomponents.PositionComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager + Velocities *stdcomponents.VelocityComponentManager } func (s *CollisionResolutionSystem) Init() {} @@ -67,7 +68,34 @@ func (s *CollisionResolutionSystem) Run(dt time.Duration) { p2.XY.X, p2.XY.Y = p2d.X, p2d.Y } - // Apply forces or velocity changes + // Apply impulse resolution + velocity1 := s.Velocities.Get(collision.E1) + velocity2 := s.Velocities.Get(collision.E2) + + if velocity1 == nil || velocity2 == nil { + return true + } + + relativeVelocity := velocity2.Vec2().Sub(velocity1.Vec2()) + velocityAlongNormal := relativeVelocity.Dot(collision.Normal) + + if velocityAlongNormal > 0 { + return true + } + + e := float32(1.0) // Coefficient of restitution (elasticity) + j := -(1 + e) * velocityAlongNormal + j /= 1/rigidbody1.Mass + 1/rigidbody2.Mass + + impulse := collision.Normal.Scale(j) + + if !rigidbody1.IsStatic { + velocity1.SetVec2(velocity1.Vec2().Sub(impulse.Scale(1 / rigidbody1.Mass))) + } + + if !rigidbody2.IsStatic { + velocity2.SetVec2(velocity2.Vec2().Add(impulse.Scale(1 / rigidbody2.Mass))) + } } return true }) diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 903d6ddd..1e7ae9a3 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -17,20 +17,31 @@ func NewVelocitySystem() VelocitySystem { } type VelocitySystem struct { - Velocities *stdcomponents.VelocityComponentManager - Positions *stdcomponents.PositionComponentManager + Velocities *stdcomponents.VelocityComponentManager + Positions *stdcomponents.PositionComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager } func (s *VelocitySystem) Init() {} + func (s *VelocitySystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) + dampingFactor := float32(0.98) // Damping factor for velocity + s.Velocities.EachEntity(func(e ecs.Entity) bool { velocity := s.Velocities.Get(e) position := s.Positions.Get(e) + rigidbody := s.RigidBodies.Get(e) position.XY.X += velocity.X * dtSec position.XY.Y += velocity.Y * dtSec + + if rigidbody != nil && !rigidbody.IsStatic { + velocity.X *= dampingFactor + velocity.Y *= dampingFactor + } return true }) } + func (s *VelocitySystem) Destroy() {} From f204b3470671bb671f0984dda83c3147d4f3306a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 26 Mar 2025 16:11:21 +0300 Subject: [PATCH 072/196] upd chatting --- examples/new-api/entities/asteroid.go | 26 +++++----- examples/new-api/entities/bullet.go | 7 ++- examples/new-api/systems/asterodd.go | 2 +- examples/new-api/systems/collision-handler.go | 47 ++++++++++--------- examples/new-api/systems/spaceship-intents.go | 2 + pkg/bvh/tree.go | 2 +- pkg/collision/epa.go | 4 +- stdcomponents/colliders.go | 4 +- 8 files changed, 51 insertions(+), 43 deletions(-) diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index 64f5b159..79d26263 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -46,34 +46,34 @@ func CreateAsteroid( scaleFactor float32, velocityX, velocityY float32, ) ecs.Entity { - bullet := props.EntityManager.Create() - props.Positions.Create(bullet, stdcomponents.Position{ + e := props.EntityManager.Create() + props.Positions.Create(e, stdcomponents.Position{ XY: vectors.Vec2{ X: posX, Y: posY, }, }) - props.Rotations.Create(bullet, stdcomponents.Rotation{}.SetFromDegrees(angle)) - props.Scales.Create(bullet, stdcomponents.Scale{ + props.Rotations.Create(e, stdcomponents.Rotation{}.SetFromDegrees(angle)) + props.Scales.Create(e, stdcomponents.Scale{ XY: vectors.Vec2{ X: 1 * scaleFactor, Y: 1 * scaleFactor, }, }) - props.Velocities.Create(bullet, stdcomponents.Velocity{ + props.Velocities.Create(e, stdcomponents.Velocity{ X: velocityX, Y: velocityY, }) - props.CircleColliders.Create(bullet, stdcomponents.CircleCollider{ + props.CircleColliders.Create(e, stdcomponents.CircleCollider{ Radius: 24, Offset: vectors.Vec2{ X: 0, Y: 0, }, Layer: config.EnemyCollisionLayer, - Mask: 0, + Mask: 1< 5000 { - s.EntityManager.Delete(e) - } - - if hp.Hp <= 0 { - s.EntityManager.Delete(e) - } - - return true - }) - - s.BulletTags.EachEntity(func(entity ecs.Entity) bool { - pos := s.Positions.Get(entity) - if pos.XY.Y > 5000 || pos.XY.Y < 0 || pos.XY.X > 5000 || pos.XY.X < 0 { - s.EntityManager.Delete(entity) - } - return true - }) - s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { switch collision.State { case stdcomponents.CollisionStateEnter: @@ -79,6 +56,9 @@ func (s *CollisionHandlerSystem) Run(dt time.Duration) { if s.checkPlayerCollisionEnter(collision.E1, collision.E2) { return true } + if s.checkAsteroidCollisionEnter(collision.E1, collision.E2) { + return true + } case stdcomponents.CollisionStateExit: default: @@ -89,6 +69,22 @@ func (s *CollisionHandlerSystem) Run(dt time.Duration) { } func (s *CollisionHandlerSystem) Destroy() {} +func (s *CollisionHandlerSystem) checkAsteroidCollisionEnter(e1, e2 ecs.Entity) bool { + e1Tag := s.AsteroidTags.Get(e1) + if e1Tag == nil { + return false + } + + wallTag := s.WallTags.Get(e2) + if wallTag != nil { + hp := s.Hps.Get(e1) + hp.Hp = 0 + return true + } + + return false +} + func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bool { e1Tag := s.PlayerTags.Get(e1) e2Tag := s.PlayerTags.Get(e2) @@ -150,6 +146,11 @@ func (s *CollisionHandlerSystem) checkBulletCollisionEnter(e1, e2 ecs.Entity) bo bulletHp.Hp -= 1 return true } + wallTag := s.WallTags.Get(e2) + if wallTag != nil { + bulletHp.Hp = 0 + return true + } } else if e2Tag != nil { // this is a bullet bulletHp := s.Hps.Get(e2) diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index 2d916e38..f6ad3a79 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -39,6 +39,7 @@ type SpaceshipIntentsSystem struct { CircleColliders *stdcomponents.CircleColliderComponentManager BulletTags *components.BulletTagComponentManager Sprites *stdcomponents.SpriteComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager Weapons *components.WeaponComponentManager Hps *components.HpComponentManager moveSpeed float32 @@ -107,6 +108,7 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { Scales: s.Scales, Velocities: s.Velocities, CircleColliders: s.CircleColliders, + RigidBodies: s.RigidBodies, Sprites: s.Sprites, BulletTags: s.BulletTags, Hps: s.Hps, diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index a05f2048..adc1b1ce 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -71,7 +71,7 @@ func (t *Tree2D) Query(aabb stdcomponents.AABB, handler func(entity ecs.Entity)) for len(stack) > 0 { nodeIndex := stack[len(stack)-1] stack = stack[:len(stack)-1] - node := t.nodes[nodeIndex] + node := &t.nodes[nodeIndex] // Early exit if no AABB overlap if !t.aabbOverlap(&aabb, &node.Bounds) { diff --git a/pkg/collision/epa.go b/pkg/collision/epa.go index 02dc19b0..6d724dd2 100644 --- a/pkg/collision/epa.go +++ b/pkg/collision/epa.go @@ -35,7 +35,7 @@ func EPA( ) (vectors.Vec2, float32) { polytope := simplex.toPolytope(make([]vectors.Vec2, 0, 6)) - for range maxIterations { + for { edge := findClosestEdge(polytope) point := minkowskiSupport2d(a, b, transformA, transformB, edge.normal) distance := point.Dot(edge.normal) @@ -47,7 +47,7 @@ func EPA( polytope = append(polytope[:edge.index], append([]vectors.Vec2{point}, polytope[edge.index:]...)...) } - panic("EPA infinite loop") + //panic("EPA infinite loop") } func findClosestEdge(polytope []vectors.Vec2) closestEdge { diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 8038b58c..f061dd62 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -92,8 +92,8 @@ func (c *CircleCollider) GetSupport(direction vectors.Vec2, transform *Transform X: c.Radius * float32(math.Cos(angle)), Y: c.Radius * float32(math.Sin(angle)), } - scaledOffset := c.Offset.Mul(transform.Scale) - return transform.Position.Sub(scaledOffset).Add(rotatedRadius.Mul(transform.Scale)) + radiusWithOffset := rotatedRadius.Sub(c.Offset).Mul(transform.Scale) + return transform.Position.Add(radiusWithOffset) } type CircleColliderComponentManager = ecs.ComponentManager[CircleCollider] From 5832860fd4b40149a17756262eb50a060ffab29f Mon Sep 17 00:00:00 2001 From: bitver Date: Wed, 26 Mar 2025 18:36:51 +0500 Subject: [PATCH 073/196] rigid body mass fun --- examples/new-api/instances/system-list.go | 2 ++ examples/new-api/scenes/assterodd-scene.go | 6 ++++ examples/new-api/systems/damping.go | 42 ++++++++++++++++++++++ stdcomponents/ids.go | 1 - stdsystems/velocity.go | 7 ---- 5 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 examples/new-api/systems/damping.go diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index acfaaeae..d433593b 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -46,6 +46,7 @@ func NewSystemList() SystemList { Audio: systems.NewAudioSystem(), + DampingSystem: systems.NewDampingSystem(), AssteroddSystem: systems.NewAssteroddSystem(), CollisionHandler: systems.NewCollisionHandlerSystem(), SpaceshipIntents: systems.NewSpaceshipIntentsSystem(), @@ -80,6 +81,7 @@ type SystemList struct { Audio systems.AudioSystem + DampingSystem systems.DampingSystem AssteroddSystem systems.AssteroddSystem CollisionHandler systems.CollisionHandlerSystem SpaceshipIntents systems.SpaceshipIntentsSystem diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index 2cb15e31..962c8da4 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -76,6 +76,7 @@ func (s *AssteroddScene) Init() { s.World.Systems.ColliderSystem.Init() // Scenes + s.World.Systems.DampingSystem.Init() s.World.Systems.SpaceSpawner.Init() s.World.Systems.AssteroddSystem.Init() s.World.Systems.SpaceshipIntents.Init() @@ -109,6 +110,7 @@ func (s *AssteroddScene) Update(dt time.Duration) gomp.SceneId { func (s *AssteroddScene) FixedUpdate(dt time.Duration) { s.World.Systems.SpaceshipIntents.Run(dt) s.World.Systems.Velocity.Run(dt) + s.World.Systems.DampingSystem.Run(dt) s.World.Systems.SpaceSpawner.Run(dt) s.World.Systems.CollisionDetectionBVH.Run(dt) s.World.Systems.CollisionResolution.Run(dt) @@ -145,6 +147,10 @@ func (s *AssteroddScene) Destroy() { s.World.Systems.CollisionDetectionBVH.Destroy() s.World.Systems.CollisionResolution.Destroy() + s.World.Systems.CollisionHandler.Destroy() + s.World.Systems.Hp.Destroy() + s.World.Systems.Velocity.Destroy() + s.World.Systems.DampingSystem.Destroy() // Animation s.World.Systems.AnimationSpriteMatrix.Destroy() diff --git a/examples/new-api/systems/damping.go b/examples/new-api/systems/damping.go new file mode 100644 index 00000000..609c81aa --- /dev/null +++ b/examples/new-api/systems/damping.go @@ -0,0 +1,42 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package systems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewDampingSystem() DampingSystem { + return DampingSystem{} +} + +type DampingSystem struct { + Velocities *stdcomponents.VelocityComponentManager + Positions *stdcomponents.PositionComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager +} + +func (s *DampingSystem) Init() {} + +func (s *DampingSystem) Run(dt time.Duration) { + dampingFactor := float32(0.98) // Damping factor for velocity + + s.Velocities.EachEntity(func(e ecs.Entity) bool { + velocity := s.Velocities.Get(e) + rigidbody := s.RigidBodies.Get(e) + + if rigidbody != nil && !rigidbody.IsStatic { + velocity.X *= dampingFactor + velocity.Y *= dampingFactor + } + return true + }) +} + +func (s *DampingSystem) Destroy() {} diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 2d5f67c3..063e8bd5 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -37,6 +37,5 @@ const ( SpatialIndexComponentId AABBComponentId RigidBodyComponentId - MassComponentId StdComponentIds ) diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 1e7ae9a3..4ec1ce80 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -26,20 +26,13 @@ func (s *VelocitySystem) Init() {} func (s *VelocitySystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - dampingFactor := float32(0.98) // Damping factor for velocity s.Velocities.EachEntity(func(e ecs.Entity) bool { velocity := s.Velocities.Get(e) position := s.Positions.Get(e) - rigidbody := s.RigidBodies.Get(e) position.XY.X += velocity.X * dtSec position.XY.Y += velocity.Y * dtSec - - if rigidbody != nil && !rigidbody.IsStatic { - velocity.X *= dampingFactor - velocity.Y *= dampingFactor - } return true }) } From dcea177d99193c317268d89480fc1204f2da2517 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 26 Mar 2025 22:50:13 +0300 Subject: [PATCH 074/196] optimize collision detection system --- examples/new-api/entities/asteroid.go | 2 +- examples/new-api/entities/bullet.go | 2 +- examples/new-api/entities/spaceship.go | 2 +- examples/new-api/systems/spaceship-intents.go | 2 +- go.mod | 3 +- go.sum | 22 +++ pkg/bvh/tree.go | 74 +++++---- pkg/collision/epa.go | 13 +- pkg/ecs/paged-array.go | 4 +- stdsystems/collision-detection-bvh.go | 141 ++++++++---------- stdsystems/debug.go | 10 +- taskfile.yml | 4 + 12 files changed, 159 insertions(+), 120 deletions(-) diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index 79d26263..7fc72362 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -65,7 +65,7 @@ func CreateAsteroid( Y: velocityY, }) props.CircleColliders.Create(e, stdcomponents.CircleCollider{ - Radius: 24, + Radius: 20, Offset: vectors.Vec2{ X: 0, Y: 0, diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 0f22ca63..242ca41b 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -63,7 +63,7 @@ func CreateBullet( Y: velocityY, }) props.CircleColliders.Create(bullet, stdcomponents.CircleCollider{ - Radius: 8, + Radius: 6, Offset: vectors.Vec2{ X: 0, Y: 0, diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go index 24028a05..f21778c4 100644 --- a/examples/new-api/entities/spaceship.go +++ b/examples/new-api/entities/spaceship.go @@ -86,7 +86,7 @@ func CreateSpaceShip( Y: 16, }, Layer: config.PlayerCollisionLayer, - Mask: 1< 0 { - nodeIndex := stack[len(stack)-1] - stack = stack[:len(stack)-1] - node := &t.nodes[nodeIndex] + for stackPtr > 0 { + stackPtr-- + nodeIndex := stack[stackPtr] + node := t.nodes.Get(nodeIndex) // Early exit if no AABB overlap - if !t.aabbOverlap(&aabb, &node.Bounds) { + bounds := &node.Bounds + if bounds.Max.X < aabb.Min.X || bounds.Min.X > aabb.Max.X || + bounds.Max.Y < aabb.Min.Y || bounds.Min.Y > aabb.Max.Y { continue } - if node.isLeaf() { + if node.Left == -1 && node.Right == -1 { // Check detailed collision with node.Entity - // (Same as your existing collision logic) - handler(node.Entity) + result = append(result, node.Entity) } else { - // Push children to stack - stack = append(stack, node.Right, node.Left) + // Push child indices (right and left) onto the stack. + stack[stackPtr] = node.Right + stack[stackPtr+1] = node.Left + stackPtr += 2 } } + + return result } -func (t *Tree2D) AddComponent(entity ecs.Entity, aabbs stdcomponents.AABB) { +func (t *Tree2D) AddComponent(entity ecs.Entity, aabbs *stdcomponents.AABB) { t.components = append(t.components, treeComponent{ Entity: entity, AABB: aabbs, @@ -110,23 +118,21 @@ type Task struct { } func (t *Tree2D) Build() { - if cap(t.nodes) < len(t.components)*2 { - t.nodes = make([]Node, 0, len(t.components)*2) - } - t.nodes = t.nodes[:0] + t.nodes.Reset() // Create leaf nodes leaves := make([]Node, len(t.components)) for i := range t.components { - aabb := t.components[i].AABB + component := &t.components[i] + aabb := component.AABB center := aabb.Min.Add(aabb.Max).Scale(0.5) code := t.morton2D(center.X, center.Y) leaves[i] = Node{ Left: -1, Right: -1, MortonCode: code, - Entity: t.components[i].Entity, - Bounds: aabb, + Entity: component.Entity, + Bounds: *aabb, } } t.components = t.components[:0] @@ -136,7 +142,9 @@ func (t *Tree2D) Build() { return int(a.MortonCode) - int(b.MortonCode) }) - t.nodes = append(t.nodes, leaves...) + for i := range leaves { + t.nodes.Append(leaves[i]) + } // Stack-based hierarchy construction var resultStack []int @@ -169,14 +177,16 @@ func (t *Tree2D) Build() { resultStack = resultStack[:len(resultStack)-1] // Create parent node and append to t.nodes + leftBounds := &t.nodes.Get(left).Bounds + rightBounds := &t.nodes.Get(right).Bounds parent := Node{ Left: left, Right: right, - Bounds: t.mergeAABB(&t.nodes[left].Bounds, &t.nodes[right].Bounds), + Bounds: t.mergeAABB(leftBounds, rightBounds), } - t.nodes = append(t.nodes, parent) + t.nodes.Append(parent) // Push parent index to result stack - resultStack = append(resultStack, len(t.nodes)-1) + resultStack = append(resultStack, t.nodes.Len()-1) } } @@ -189,8 +199,8 @@ func (t *Tree2D) Build() { // findSplit finds the position where the highest bit changes func (t *Tree2D) findSplit(start, end int) int { // Identical Morton sortedMortonCodes => split the range in the middle. - first := t.nodes[start].MortonCode - last := t.nodes[end].MortonCode + first := t.nodes.Get(start).MortonCode + last := t.nodes.Get(end).MortonCode if first == last { return (start + end) >> 1 @@ -211,7 +221,7 @@ func (t *Tree2D) findSplit(start, end int) int { newSplit := split + step // proposed new position if newSplit < end { - splitCode := t.nodes[newSplit].MortonCode + splitCode := t.nodes.Get(newSplit).MortonCode splitPrefix := bits.LeadingZeros32(first ^ splitCode) if splitPrefix > commonPrefix { split = newSplit diff --git a/pkg/collision/epa.go b/pkg/collision/epa.go index 6d724dd2..0cc45a66 100644 --- a/pkg/collision/epa.go +++ b/pkg/collision/epa.go @@ -35,7 +35,11 @@ func EPA( ) (vectors.Vec2, float32) { polytope := simplex.toPolytope(make([]vectors.Vec2, 0, 6)) - for { + bestNormal := vectors.Vec2{} + bestDistance := float32(math.MaxFloat32) + bestTolerance := float32(math.MaxFloat32) + + for range maxIterations { edge := findClosestEdge(polytope) point := minkowskiSupport2d(a, b, transformA, transformB, edge.normal) distance := point.Dot(edge.normal) @@ -43,11 +47,16 @@ func EPA( if tolerance < epaMaxTolerance { return edge.normal, distance } + if tolerance < bestTolerance { + bestTolerance = tolerance + bestNormal = edge.normal + bestDistance = distance + } polytope = append(polytope[:edge.index], append([]vectors.Vec2{point}, polytope[edge.index:]...)...) } - //panic("EPA infinite loop") + return bestNormal, bestDistance } func findClosestEdge(polytope []vectors.Vec2) closestEdge { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index fee6c93a..05891728 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -101,8 +101,8 @@ func (a *PagedArray[T]) SoftReduce() { } func (a *PagedArray[T]) Reset() { - for range a.currentPageIndex { - page := &a.book[a.currentPageIndex] + for i := 0; i <= a.currentPageIndex; i++ { + page := &a.book[i] page.len = 0 } diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 70312f60..8766762c 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -25,9 +25,12 @@ import ( "time" ) +var maxNumWorkers = runtime.NumCPU() - 2 + func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { return CollisionDetectionBVHSystem{ activeCollisions: make(map[CollisionPair]ecs.Entity), + collisionEvents: make([]ecs.PagedArray[CollisionEvent], maxNumWorkers), } } @@ -47,11 +50,17 @@ type CollisionDetectionBVHSystem struct { trees []bvh.Tree2D treesLookup map[stdcomponents.CollisionLayer]int + collisionEvents []ecs.PagedArray[CollisionEvent] + activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities currentCollisions map[CollisionPair]struct{} } -func (s *CollisionDetectionBVHSystem) Init() {} +func (s *CollisionDetectionBVHSystem) Init() { + for i := range maxNumWorkers { + s.collisionEvents[i] = ecs.NewPagedArray[CollisionEvent]() + } +} func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { s.currentCollisions = make(map[CollisionPair]struct{}) defer s.processExitStates() @@ -76,7 +85,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { s.treesLookup[layer] = treeId } - s.trees[treeId].AddComponent(entity, *aabb) + s.trees[treeId].AddComponent(entity, aabb) return true }) @@ -92,13 +101,43 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } wg.Wait() - // Create collision channel - collisionChan := make(chan CollisionEvent, 4096*4) - doneChan := make(chan struct{}) + entities := s.AABB.RawEntities(make([]ecs.Entity, 0, s.AABB.Len())) + + s.findEntityCollisions(entities) +} + +func (s *CollisionDetectionBVHSystem) Destroy() {} + +func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity) { + var wg sync.WaitGroup + entitiesLength := len(entities) + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(entitiesLength/128, maxNumWorkers), 1) + chunkSize := entitiesLength / numWorkers + + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = entitiesLength + } + + go func(start int, end int, id int) { + defer wg.Done() + + for i := range entities[start:end] { + entity := entities[i+startIndex] + s.broadPhase(entity, id) + } + }(startIndex, endIndex, workedId) + } + // Wait for workers and close collision channel + wg.Wait() - // Start result collector - go func() { - for event := range collisionChan { + for i := range s.collisionEvents { + events := &s.collisionEvents[i] + events.AllData(func(event *CollisionEvent) bool { pair := CollisionPair{event.entityA, event.entityB} s.currentCollisions[pair] = struct{}{} displacement := event.normal.Scale(event.depth) @@ -129,51 +168,13 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { position.XY.X = pos.X position.XY.Y = pos.Y } - } - close(doneChan) - }() - - entities := s.AABB.RawEntities(make([]ecs.Entity, 0, s.AABB.Len())) - aabbs := s.AABB.RawComponents(make([]stdcomponents.AABB, 0, s.AABB.Len())) - - s.findEntityCollisions(entities, aabbs, collisionChan) - - close(collisionChan) - <-doneChan // Wait for result collector -} - -func (s *CollisionDetectionBVHSystem) Destroy() {} - -func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, collisionChan chan<- CollisionEvent) { - var wg sync.WaitGroup - maxNumWorkers := runtime.NumCPU() - entitiesLength := len(entities) - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(entitiesLength/128, maxNumWorkers), 1) - chunkSize := entitiesLength / numWorkers - - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = entitiesLength - } - - go func(start int, end int) { - defer wg.Done() - - for i := range entities[start:end] { - entity := entities[i+startIndex] - s.broadPhase(entity, collisionChan) - } - }(startIndex, endIndex) + return true + }) + s.collisionEvents[i].Reset() } - // Wait for workers and close collision channel - wg.Wait() } -func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, collisionChan chan<- CollisionEvent) { +func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, workerId int) { colliderA := s.GenericCollider.Get(entityA) aabb := s.AABB.Get(entityA) @@ -188,17 +189,19 @@ func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, collisionCh } // Traverse this BVH tree for potential collisions - tree.Query(*aabb, func(entityB ecs.Entity) { + result := tree.Query(aabb, make([]ecs.Entity, 0, 64)) + + for _, entityB := range result { if entityA == entityB { - return + continue } colliderB := s.GenericCollider.Get(entityB) collision, ok := s.narrowPhase(colliderA, colliderB, entityA, entityB) if ok { - collisionChan <- collision + s.collisionEvents[workerId].Append(collision) } - }) + } } } @@ -207,19 +210,19 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(colliderA, colliderB *stdcompo colB := s.getGjkCollider(colliderB, entityB) posA := s.Positions.Get(entityA) posB := s.Positions.Get(entityB) - scaleA := s.getScaleOrDefault(entityA) - scaleB := s.getScaleOrDefault(entityB) - rotA := s.getRotationOrDefault(entityA) - rotB := s.getRotationOrDefault(entityB) + scaleA := s.Scales.Get(entityA) + scaleB := s.Scales.Get(entityB) + rotA := s.Rotations.Get(entityA) + rotB := s.Rotations.Get(entityB) transformA := stdcomponents.Transform2d{ Position: posA.XY, Rotation: rotA.Angle, - Scale: scaleA, + Scale: scaleA.XY, } transformB := stdcomponents.Transform2d{ Position: posB.XY, Rotation: rotB.Angle, - Scale: scaleB, + Scale: scaleB.XY, } // First detect collision using GJK @@ -240,24 +243,6 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(colliderA, colliderB *stdcompo }, true } -func (s *CollisionDetectionBVHSystem) getScaleOrDefault(entity ecs.Entity) vectors.Vec2 { - scale := s.Scales.Get(entity) - if scale != nil { - return scale.XY // Dereference the component pointer - } - // Return default scale of 1 if component doesn't exist - return vectors.Vec2{X: 1, Y: 1} -} - -func (s *CollisionDetectionBVHSystem) getRotationOrDefault(entity ecs.Entity) stdcomponents.Rotation { - rotation := s.Rotations.Get(entity) - if rotation != nil { - return *rotation // Dereference the component pointer - } - // Return default zero rotation if component doesn't exist - return stdcomponents.Rotation{Angle: 0} -} - func (s *CollisionDetectionBVHSystem) processExitStates() { for pair, proxy := range s.activeCollisions { if _, exists := s.currentCollisions[pair]; !exists { diff --git a/stdsystems/debug.go b/stdsystems/debug.go index d9c62e28..1137ddf3 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -8,8 +8,11 @@ package stdsystems import ( "fmt" + //"github.com/felixge/fgprof" rl "github.com/gen2brain/raylib-go/raylib" "log" + //"net/http" + _ "net/http/pprof" "os" "runtime/pprof" ) @@ -22,7 +25,12 @@ type DebugSystem struct { pprofEnabled bool } -func (s *DebugSystem) Init() {} +func (s *DebugSystem) Init() { + //http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler()) + //go func() { + // log.Println(http.ListenAndServe(":6060", nil)) + //}() +} func (s *DebugSystem) Run() { if rl.IsKeyPressed(rl.KeyF9) { if s.pprofEnabled { diff --git a/taskfile.yml b/taskfile.yml index 05975f00..fd27744f 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -30,6 +30,10 @@ tasks: cmds: - protoc --go_out=. internal/**/*.proto + gpprof-cpu: + cmds: + - go tool pprof --http=:6061 http://localhost:6060/debug/fgprof?seconds=20 + pprof-cpu: cmds: - go tool pprof -http=":8000" ./cpu.out From 95a7805717d9006144066fd0fa561c35fdc904cb Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 27 Mar 2025 01:54:40 +0300 Subject: [PATCH 075/196] implement circle to circle collision strategy --- stdsystems/collision-detection-bvh.go | 44 +++++++++++++++++++-------- vectors/vector2.go | 4 +++ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 8766762c..7d77b712 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -125,6 +125,8 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity go func(start int, end int, id int) { defer wg.Done() + runtime.LockOSThread() + defer runtime.UnlockOSThread() for i := range entities[start:end] { entity := entities[i+startIndex] @@ -178,6 +180,8 @@ func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, workerId in colliderA := s.GenericCollider.Get(entityA) aabb := s.AABB.Get(entityA) + result := make([]ecs.Entity, 0, 64) + // Iterate through all trees for treeIndex := range s.trees { tree := &s.trees[treeIndex] @@ -189,25 +193,22 @@ func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, workerId in } // Traverse this BVH tree for potential collisions - result := tree.Query(aabb, make([]ecs.Entity, 0, 64)) + result = tree.Query(aabb, result) + } - for _, entityB := range result { - if entityA == entityB { - continue - } + for _, entityB := range result { + if entityA == entityB { + continue + } - colliderB := s.GenericCollider.Get(entityB) - collision, ok := s.narrowPhase(colliderA, colliderB, entityA, entityB) - if ok { - s.collisionEvents[workerId].Append(collision) - } + colliderB := s.GenericCollider.Get(entityB) + if collision, ok := s.narrowPhase(colliderA, colliderB, entityA, entityB); ok { + s.collisionEvents[workerId].Append(collision) } } } func (s *CollisionDetectionBVHSystem) narrowPhase(colliderA, colliderB *stdcomponents.GenericCollider, entityA, entityB ecs.Entity) (e CollisionEvent, ok bool) { - colA := s.getGjkCollider(colliderA, entityA) - colB := s.getGjkCollider(colliderB, entityB) posA := s.Positions.Get(entityA) posB := s.Positions.Get(entityB) scaleA := s.Scales.Get(entityA) @@ -225,6 +226,25 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(colliderA, colliderB *stdcompo Scale: scaleB.XY, } + circleA := s.CircleColliders.Get(entityA) + circleB := s.CircleColliders.Get(entityB) + if circleA != nil && circleB != nil { + radiusA := circleA.Radius * scaleA.XY.X + radiusB := circleB.Radius * scaleB.XY.X + if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { + return CollisionEvent{ + entityA: entityA, + entityB: entityB, + position: transformA.Position, + normal: transformB.Position.Sub(transformA.Position).Normalize(), + depth: radiusA + radiusB - transformB.Position.Distance(transformA.Position), + }, true + } + } + + // GJK strategy + colA := s.getGjkCollider(colliderA, entityA) + colB := s.getGjkCollider(colliderB, entityB) // First detect collision using GJK simplex, collision := gjk.CheckCollision(colA, colB, &transformA, &transformB) if !collision { diff --git a/vectors/vector2.go b/vectors/vector2.go index ade5c963..d3a0f09f 100644 --- a/vectors/vector2.go +++ b/vectors/vector2.go @@ -58,6 +58,10 @@ func (v Vec2) Length() float32 { return float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y))) } +func (v Vec2) Distance(other Vec2) float32 { + return v.Sub(other).Length() +} + func (v Vec2) Normalize() Vec2 { return v.Scale(1 / v.Length()) } From 66bb6a59bab8b8e1a87e312ef5790aa915d116df Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 27 Mar 2025 13:40:26 +0300 Subject: [PATCH 076/196] upd tree --- pkg/bvh/tree.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index a7f39005..c257e316 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -78,8 +78,11 @@ func (t *Tree2D) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Enti // Early exit if no AABB overlap bounds := &node.Bounds - if bounds.Max.X < aabb.Min.X || bounds.Min.X > aabb.Max.X || - bounds.Max.Y < aabb.Min.Y || bounds.Min.Y > aabb.Max.Y { + if bounds.Max.X < aabb.Min.X || bounds.Min.X > aabb.Max.X { + continue + } + + if bounds.Max.Y < aabb.Min.Y || bounds.Min.Y > aabb.Max.Y { continue } From 6d816f2bcd25b6bd28da45fb645f64b9fe813e9f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 27 Mar 2025 14:51:34 +0300 Subject: [PATCH 077/196] update bvhtree --- pkg/bvh/components.go | 11 + pkg/bvh/tree-ds.go | 298 ++++++++++++++++++++++++++ pkg/bvh/tree.go | 5 - stdsystems/collision-detection-bvh.go | 8 +- 4 files changed, 312 insertions(+), 10 deletions(-) create mode 100644 pkg/bvh/components.go create mode 100644 pkg/bvh/tree-ds.go diff --git a/pkg/bvh/components.go b/pkg/bvh/components.go new file mode 100644 index 00000000..527f83e3 --- /dev/null +++ b/pkg/bvh/components.go @@ -0,0 +1,11 @@ +package bvh + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +type treeComponent struct { + Entity ecs.Entity + AABB *stdcomponents.AABB +} diff --git a/pkg/bvh/tree-ds.go b/pkg/bvh/tree-ds.go new file mode 100644 index 00000000..fa3ff20c --- /dev/null +++ b/pkg/bvh/tree-ds.go @@ -0,0 +1,298 @@ +package bvh + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "math" + "math/bits" + "slices" +) + +func NewTree2Du(layer stdcomponents.CollisionLayer, prealloc int) Tree2Du { + return Tree2Du{ + layer: layer, + components: make([]treeComponent, 0, prealloc), + innerNodes: make([]InnerNode, 0, prealloc), + leafNodes: make([]LeafNode, 0, prealloc), + mortonCodes: make([]uint32, 0, prealloc), + rootIndex: -1, + } +} + +type Tree2Du struct { + layer stdcomponents.CollisionLayer + innerNodes []InnerNode // Отдельный массив для внутренних нод + components []treeComponent + leafNodes []LeafNode // Отдельный массив для листьев + mortonCodes []uint32 // Кэшированные Morton codes для листьев + rootIndex int +} + +type LeafNode struct { + Entity ecs.Entity + Bounds stdcomponents.AABB +} + +type InnerNode struct { + Left, Right int // Отрицательные индексы = leafNodes[-(index+1)] + Bounds stdcomponents.AABB + SplitAxis int // 0 = X, 1 = Y для SAH оптимизации +} + +func (t *Tree2Du) AddComponent(entity ecs.Entity, aabbs *stdcomponents.AABB) { + t.components = append(t.components, treeComponent{ + Entity: entity, + AABB: aabbs, + }) +} + +func (t *Tree2Du) Layer() stdcomponents.CollisionLayer { + return t.layer +} + +func (t *Tree2Du) Build() { + t.innerNodes = t.innerNodes[:0] + t.leafNodes = t.leafNodes[:0] + t.mortonCodes = t.mortonCodes[:0] + + if len(t.components) == 0 { + t.rootIndex = -1 + return + } + + // 1. Создаем листья с Morton codes + leaves := make([]LeafNode, len(t.components)) + t.mortonCodes = make([]uint32, len(t.components)) + + for i := range t.components { + component := &t.components[i] + aabb := component.AABB + center := aabb.Min.Add(aabb.Max).Scale(0.5) + leaves[i] = LeafNode{ + Entity: component.Entity, + Bounds: *aabb, + } + t.mortonCodes[i] = t.morton2D(center.X, center.Y) + } + t.components = t.components[:0] + + // 2. Сортируем листья по Morton code + indices := make([]int, len(leaves)) + for i := range indices { + indices[i] = i + } + slices.SortFunc(indices, func(a, b int) int { + return int(t.mortonCodes[a]) - int(t.mortonCodes[b]) + }) + + // Реорганизуем данные в соответствии с сортировкой + sortedLeaves := make([]LeafNode, len(leaves)) + sortedCodes := make([]uint32, len(leaves)) + for i, idx := range indices { + sortedLeaves[i] = leaves[idx] + sortedCodes[i] = t.mortonCodes[idx] + } + t.leafNodes = sortedLeaves + t.mortonCodes = sortedCodes + + // 3. Строим иерархию используя бинарное разбиение + if len(t.leafNodes) == 1 { + t.rootIndex = -1 // Помечаем как единственный лист + return + } + + // Рекурсивное построение через стек задач + type buildTask struct { + start, end int + parentPtr *int + } + + var stack []buildTask + rootTask := buildTask{0, len(t.leafNodes) - 1, &t.rootIndex} + stack = append(stack, rootTask) + + for len(stack) > 0 { + task := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + start, end := task.start, task.end + if start == end { + *task.parentPtr = -start - 1 // Листовой узел + continue + } + + // Находим точку разделения по Morton codes + split := t.findSplit(start, end) + + // Создаем внутреннюю ноду + nodeIndex := len(t.innerNodes) + t.innerNodes = append(t.innerNodes, InnerNode{}) + *task.parentPtr = nodeIndex + + // Обрабатываем детей + stack = append(stack, buildTask{ + start: split + 1, + end: end, + parentPtr: &t.innerNodes[nodeIndex].Right, + }) + stack = append(stack, buildTask{ + start: start, + end: split, + parentPtr: &t.innerNodes[nodeIndex].Left, + }) + } + + // 4. Вычисляем AABB для всех нод + t.computeBounds(t.rootIndex) +} + +func (t *Tree2Du) findSplit(start, end int) int { + first := t.mortonCodes[start] + last := t.mortonCodes[end] + if first == last { + return (start + end) >> 1 + } + + commonPrefix := bits.LeadingZeros32(first ^ last) + split := start + step := end - start + + for { + step = (step + 1) >> 1 + newSplit := split + step + + if newSplit < end { + splitCode := t.mortonCodes[newSplit] + splitPrefix := bits.LeadingZeros32(first ^ splitCode) + if splitPrefix > commonPrefix { + split = newSplit + } + } + + if step <= 1 { + break + } + } + + return split +} + +func (t *Tree2Du) computeBounds(nodeIndex int) stdcomponents.AABB { + if nodeIndex < 0 { // Лист + leaf := t.leafNodes[-nodeIndex-1] + return leaf.Bounds + } + + node := &t.innerNodes[nodeIndex] + leftBounds := t.computeBounds(node.Left) + rightBounds := t.computeBounds(node.Right) + node.Bounds = t.mergeAABB(&leftBounds, &rightBounds) + + // Определяем ось разделения по Morton codes + leftCode := t.getMortonCode(node.Left) + rightCode := t.getMortonCode(node.Right) + diff := leftCode ^ rightCode + node.SplitAxis = bits.TrailingZeros32(diff) % 2 + + return node.Bounds +} + +func (t *Tree2Du) getMortonCode(nodeIndex int) uint32 { + if nodeIndex < 0 { + return t.mortonCodes[-nodeIndex-1] + } + // Для внутренних нод возвращаем код разделения + return t.mortonCodes[t.findSplitForNode(nodeIndex)] +} + +func (t *Tree2Du) findSplitForNode(nodeIndex int) int { + node := t.innerNodes[nodeIndex] + left := node.Left + for left >= 0 { + left = t.innerNodes[left].Left + } + return -left - 1 +} + +func (t *Tree2Du) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity { + if t.rootIndex == -1 { + if len(t.leafNodes) == 1 && t.aabbOverlap(&t.leafNodes[0].Bounds, aabb) { + result = append(result, t.leafNodes[0].Entity) + } + return result + } + + stack := make([]int, 0, 32) + stack = append(stack, t.rootIndex) + + for len(stack) > 0 { + nodeIndex := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + if nodeIndex >= 0 { // Inner node + node := t.innerNodes[nodeIndex] + + if !t.aabbOverlap(&node.Bounds, aabb) { + continue + } + + // Оптимизация порядка обхода на основе оси разделения + if (node.SplitAxis == 0 && aabb.Min.X <= node.Bounds.Min.X) || + (node.SplitAxis == 1 && aabb.Min.Y <= node.Bounds.Min.Y) { + stack = append(stack, node.Right, node.Left) + } else { + stack = append(stack, node.Left, node.Right) + } + } else { // Leaf node + leafIndex := -nodeIndex - 1 + if t.aabbOverlap(&t.leafNodes[leafIndex].Bounds, aabb) { + result = append(result, t.leafNodes[leafIndex].Entity) + } + } + } + + return result +} + +// go:inline aabbOverlap checks if two AABB intersect +func (t *Tree2Du) aabbOverlap(a, b *stdcomponents.AABB) bool { + // Check for non-overlap conditions first (early exit) + if a.Max.X < b.Min.X || a.Min.X > b.Max.X { + return false + } + if a.Max.Y < b.Min.Y || a.Min.Y > b.Max.Y { + return false + } + return true +} + +// Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit +func (t *Tree2Du) expandBits2D(v uint32) uint32 { + v = (v * 0x00010001) & 0xFF0000FF + v = (v * 0x00000101) & 0x0F00F00F + v = (v * 0x00000011) & 0xC30C30C3 + v = (v * 0x00000005) & 0x24924924 + return v +} + +// 2D Morton code for centroids coordinates in [0,1] range +func (t *Tree2Du) morton2D(x, y float32) uint32 { + xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) + yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) + return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) +} + +// mergeAABB combines two AABB +func (t *Tree2Du) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { + return stdcomponents.AABB{ + Min: vectors.Vec2{ + X: min(a.Min.X, b.Min.X), + Y: min(a.Min.Y, b.Min.Y), + }, + Max: vectors.Vec2{ + X: max(a.Max.X, b.Max.X), + Y: max(a.Max.Y, b.Max.Y), + }, + } +} diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index c257e316..c64ec019 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -51,11 +51,6 @@ type Tree2D struct { rootIndex int } -type treeComponent struct { - Entity ecs.Entity - AABB *stdcomponents.AABB -} - func (t *Tree2D) Layer() stdcomponents.CollisionLayer { return t.layer } diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 7d77b712..4b75076e 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -47,7 +47,7 @@ type CollisionDetectionBVHSystem struct { SpatialIndex *stdcomponents.SpatialIndexComponentManager AABB *stdcomponents.AABBComponentManager - trees []bvh.Tree2D + trees []bvh.Tree2Du treesLookup map[stdcomponents.CollisionLayer]int collisionEvents []ecs.PagedArray[CollisionEvent] @@ -70,7 +70,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } // Init trees - s.trees = make([]bvh.Tree2D, 0, 8) + s.trees = make([]bvh.Tree2Du, 0, 8) s.treesLookup = make(map[stdcomponents.CollisionLayer]int, 8) // Fill trees @@ -81,7 +81,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { treeId, exists := s.treesLookup[layer] if !exists { treeId = len(s.trees) - s.trees = append(s.trees, bvh.NewTree2D(layer, s.AABB.Len())) + s.trees = append(s.trees, bvh.NewTree2Du(layer, s.AABB.Len())) s.treesLookup[layer] = treeId } @@ -125,8 +125,6 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity go func(start int, end int, id int) { defer wg.Done() - runtime.LockOSThread() - defer runtime.UnlockOSThread() for i := range entities[start:end] { entity := entities[i+startIndex] From be1cb24ca3056d34b5a36f9770657e987f21aae9 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Mar 2025 00:16:00 +0300 Subject: [PATCH 078/196] upd bvhtree --- examples/new-api/systems/render-assterodd.go | 90 +++--- pkg/bvh/gen-tree.go | 255 ++++++++++++++++ pkg/bvh/tree-ds.go | 298 ------------------- stdsystems/collision-detection-bvh.go | 6 +- 4 files changed, 303 insertions(+), 346 deletions(-) create mode 100644 pkg/bvh/gen-tree.go delete mode 100644 pkg/bvh/tree-ds.go diff --git a/examples/new-api/systems/render-assterodd.go b/examples/new-api/systems/render-assterodd.go index c1efb39f..27c5a14b 100644 --- a/examples/new-api/systems/render-assterodd.go +++ b/examples/new-api/systems/render-assterodd.go @@ -109,34 +109,34 @@ func (s *RenderAssteroddSystem) render() { // ========== // DEBUG // ========== - rl.BeginMode2D(s.camera) - s.BoxColliders.EachEntity(func(e ecs.Entity) bool { - col := s.BoxColliders.Get(e) - scale := s.Scales.Get(e) - pos := s.Positions.Get(e) - rot := s.Rotations.Get(e) - - rl.DrawRectanglePro(rl.Rectangle{ - X: pos.XY.X, - Y: pos.XY.Y, - Width: col.WH.X * scale.XY.X, - Height: col.WH.Y * scale.XY.Y, - }, rl.Vector2{ - X: col.Offset.X * scale.XY.X, - Y: col.Offset.Y * scale.XY.Y, - }, float32(rot.Degrees()), rl.DarkGreen) - return true - }) - s.CircleColliders.EachEntity(func(e ecs.Entity) bool { - col := s.CircleColliders.Get(e) - scale := s.Scales.Get(e) - pos := s.Positions.Get(e) - - posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) - rl.DrawCircle(int32(posWithOffset.X), int32(posWithOffset.Y), col.Radius*scale.XY.X, rl.DarkGreen) - return true - }) - rl.EndMode2D() + //rl.BeginMode2D(s.camera) + //s.BoxColliders.EachEntity(func(e ecs.Entity) bool { + // col := s.BoxColliders.Get(e) + // scale := s.Scales.Get(e) + // pos := s.Positions.Get(e) + // rot := s.Rotations.Get(e) + // + // rl.DrawRectanglePro(rl.Rectangle{ + // X: pos.XY.X, + // Y: pos.XY.Y, + // Width: col.WH.X * scale.XY.X, + // Height: col.WH.Y * scale.XY.Y, + // }, rl.Vector2{ + // X: col.Offset.X * scale.XY.X, + // Y: col.Offset.Y * scale.XY.Y, + // }, float32(rot.Degrees()), rl.DarkGreen) + // return true + //}) + //s.CircleColliders.EachEntity(func(e ecs.Entity) bool { + // col := s.CircleColliders.Get(e) + // scale := s.Scales.Get(e) + // pos := s.Positions.Get(e) + // + // posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) + // rl.DrawCircle(int32(posWithOffset.X), int32(posWithOffset.Y), col.Radius*scale.XY.X, rl.DarkGreen) + // return true + //}) + //rl.EndMode2D() // Extract and sort entities if cap(s.renderList) < s.Renderables.Len() { @@ -194,23 +194,23 @@ func (s *RenderAssteroddSystem) render() { // ========== // DEBUG // ========== - rl.BeginMode2D(s.camera) - s.AABBs.EachEntity(func(e ecs.Entity) bool { - aabb := s.AABBs.Get(e) - rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), rl.Green) - return true - }) - s.Collisions.EachEntity(func(entity ecs.Entity) bool { - pos := s.Positions.Get(entity) - rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) - return true - }) - s.Renderables.EachEntity(func(e ecs.Entity) bool { - position := s.Positions.Get(e) - rl.DrawRectangle(int32(position.XY.X-2), int32(position.XY.Y-2), 4, 4, rl.Red) - return true - }) - rl.EndMode2D() + //rl.BeginMode2D(s.camera) + //s.AABBs.EachEntity(func(e ecs.Entity) bool { + // aabb := s.AABBs.Get(e) + // rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), rl.Green) + // return true + //}) + //s.Collisions.EachEntity(func(entity ecs.Entity) bool { + // pos := s.Positions.Get(entity) + // rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) + // return true + //}) + //s.Renderables.EachEntity(func(e ecs.Entity) bool { + // position := s.Positions.Get(e) + // rl.DrawRectangle(int32(position.XY.X-2), int32(position.XY.Y-2), 4, 4, rl.Red) + // return true + //}) + //rl.EndMode2D() } func (s *RenderAssteroddSystem) submitBatch(texID int, data []stdcomponents.RLTexturePro) { diff --git a/pkg/bvh/gen-tree.go b/pkg/bvh/gen-tree.go new file mode 100644 index 00000000..713642a6 --- /dev/null +++ b/pkg/bvh/gen-tree.go @@ -0,0 +1,255 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package bvh + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "math" + "math/bits" + "slices" +) + +type genNode struct { + childIndex int16 // if < 0 then points to leaf +} + +type genLeaf struct { + id ecs.Entity +} + +type genTreeComponent struct { + entity ecs.Entity + aabb *stdcomponents.AABB + code uint32 +} + +func NewGenTree(layer stdcomponents.CollisionLayer, prealloc int) GenTree { + return GenTree{ + nodes: make([]genNode, 0, prealloc), + aabbNodes: make([]stdcomponents.AABB, 0, prealloc), + leaves: make([]genLeaf, 0, prealloc), + aabbLeaves: make([]*stdcomponents.AABB, 0, prealloc), + codes: make([]uint32, 0, prealloc), + components: make([]genTreeComponent, 0, prealloc), + layer: layer, + } +} + +type GenTree struct { + nodes []genNode + aabbNodes []stdcomponents.AABB + + leaves []genLeaf + aabbLeaves []*stdcomponents.AABB + + codes []uint32 + + components []genTreeComponent + + layer stdcomponents.CollisionLayer +} + +func (t *GenTree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { + center := aabb.Min.Add(aabb.Max).Scale(0.5) + code := t.morton2D(center.X, center.Y) + t.components = append(t.components, genTreeComponent{ + entity: entity, + aabb: aabb, + code: code, + }) +} + +func (t *GenTree) Build() { + // Reset tree + t.nodes = t.nodes[:0] + t.aabbNodes = t.aabbNodes[:0] + t.leaves = t.leaves[:0] + t.aabbLeaves = t.aabbLeaves[:0] + + // Sort components by morton code + slices.SortFunc(t.components, func(a, b genTreeComponent) int { + return int(a.code) - int(b.code) + }) + + // Add leaves + for i := 0; i < len(t.components); i++ { + component := &t.components[i] + t.leaves = append(t.leaves, genLeaf{ + id: component.entity, + }) + t.aabbLeaves = append(t.aabbLeaves, component.aabb) + t.codes = append(t.codes, component.code) + } + t.components = t.components[:0] + + // Add root node + t.nodes = append(t.nodes, genNode{-1}) + t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) + + t.buildH(0, 0, len(t.leaves)-1) +} + +func (t *GenTree) buildH(parentIndex int, start, end int) { + if start == end { + // Is a leaf + t.nodes[parentIndex].childIndex = -int16(start) + t.aabbNodes[parentIndex] = *t.aabbLeaves[start] + return + } + + split := t.findSplit(start, end) + + // Add left node + leftIndex := len(t.nodes) + t.nodes = append(t.nodes, genNode{-1}) + t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) + + // Add right node + rightIndex := len(t.nodes) + t.nodes = append(t.nodes, genNode{-1}) + t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) + + t.nodes[parentIndex].childIndex = int16(leftIndex) + + t.buildH(leftIndex, start, split) + t.buildH(rightIndex, split+1, end) + + t.aabbNodes[parentIndex] = t.mergeAABB(&t.aabbNodes[leftIndex], &t.aabbNodes[rightIndex]) +} + +func (t *GenTree) Layer() stdcomponents.CollisionLayer { + return t.layer +} + +func (t *GenTree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity { + if len(t.nodes) == 0 { // Handle empty tree + return result + } + + // Use stack-based traversal + const stackSize = 32 + stack := [stackSize]int{} + stackPtr := 0 + stack[stackPtr] = 0 + stackPtr++ + + for stackPtr > 0 { + stackPtr-- + nodeIndex := stack[stackPtr] + a := &t.aabbNodes[nodeIndex] + b := aabb + + // Early exit if no AABB overlap + if !t.aabbOverlap(a, b) { + continue + } + + node := &t.nodes[nodeIndex] + if node.childIndex <= 0 { + // Is a leaf + index := -node.childIndex + result = append(result, t.leaves[index].id) + continue + } + + // Push child indices (right and left) onto the stack. + stack[stackPtr] = int(node.childIndex + 1) + stack[stackPtr+1] = int(node.childIndex) + stackPtr += 2 + } + + return result +} + +// go:inline aabbOverlap checks if two AABB intersect +func (t *GenTree) aabbOverlap(a, b *stdcomponents.AABB) bool { + return a.Max.X >= b.Min.X && a.Min.X <= b.Max.X && + a.Max.Y >= b.Min.Y && a.Min.Y <= b.Max.Y +} + +// findSplit finds the position where the highest bit changes +func (t *GenTree) findSplit(start, end int) int { + // Identical Morton sortedMortonCodes => split the range in the middle. + first := t.codes[start] + last := t.codes[end] + + if first == last { + return (start + end) >> 1 + } + + // Calculate the number of highest bits that are the same + // for all objects, using the count-leading-zeros intrinsic. + commonPrefix := bits.LeadingZeros32(first ^ last) + + // Use binary search to find where the next bit differs. + // Specifically, we are looking for the highest object that + // shares more than commonPrefix bits with the first one. + split := start + step := end - start + + for { + step = (step + 1) >> 1 // exponential decrease + newSplit := split + step // proposed new position + + if newSplit < end { + splitCode := t.codes[newSplit] + splitPrefix := bits.LeadingZeros32(first ^ splitCode) + if splitPrefix > commonPrefix { + split = newSplit + } + } + + if step <= 1 { + break + } + } + + return split +} + +// mergeAABB combines two AABB +func (t *GenTree) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { + return stdcomponents.AABB{ + Min: vectors.Vec2{ + X: min(a.Min.X, b.Min.X), + Y: min(a.Min.Y, b.Min.Y), + }, + Max: vectors.Vec2{ + X: max(a.Max.X, b.Max.X), + Y: max(a.Max.Y, b.Max.Y), + }, + } +} + +// Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit +func (t *GenTree) expandBits2D(v uint32) uint32 { + v = (v * 0x00010001) & 0xFF0000FF + v = (v * 0x00000101) & 0x0F00F00F + v = (v * 0x00000011) & 0xC30C30C3 + v = (v * 0x00000005) & 0x24924924 + return v +} + +// 2D Morton code for centroids coordinates in [0,1] range +func (t *GenTree) morton2D(x, y float32) uint32 { + xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) + yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) + return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) +} + +const a = 0xFF0000FF +const b = 0x030000FF diff --git a/pkg/bvh/tree-ds.go b/pkg/bvh/tree-ds.go deleted file mode 100644 index fa3ff20c..00000000 --- a/pkg/bvh/tree-ds.go +++ /dev/null @@ -1,298 +0,0 @@ -package bvh - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" - "gomp/vectors" - "math" - "math/bits" - "slices" -) - -func NewTree2Du(layer stdcomponents.CollisionLayer, prealloc int) Tree2Du { - return Tree2Du{ - layer: layer, - components: make([]treeComponent, 0, prealloc), - innerNodes: make([]InnerNode, 0, prealloc), - leafNodes: make([]LeafNode, 0, prealloc), - mortonCodes: make([]uint32, 0, prealloc), - rootIndex: -1, - } -} - -type Tree2Du struct { - layer stdcomponents.CollisionLayer - innerNodes []InnerNode // Отдельный массив для внутренних нод - components []treeComponent - leafNodes []LeafNode // Отдельный массив для листьев - mortonCodes []uint32 // Кэшированные Morton codes для листьев - rootIndex int -} - -type LeafNode struct { - Entity ecs.Entity - Bounds stdcomponents.AABB -} - -type InnerNode struct { - Left, Right int // Отрицательные индексы = leafNodes[-(index+1)] - Bounds stdcomponents.AABB - SplitAxis int // 0 = X, 1 = Y для SAH оптимизации -} - -func (t *Tree2Du) AddComponent(entity ecs.Entity, aabbs *stdcomponents.AABB) { - t.components = append(t.components, treeComponent{ - Entity: entity, - AABB: aabbs, - }) -} - -func (t *Tree2Du) Layer() stdcomponents.CollisionLayer { - return t.layer -} - -func (t *Tree2Du) Build() { - t.innerNodes = t.innerNodes[:0] - t.leafNodes = t.leafNodes[:0] - t.mortonCodes = t.mortonCodes[:0] - - if len(t.components) == 0 { - t.rootIndex = -1 - return - } - - // 1. Создаем листья с Morton codes - leaves := make([]LeafNode, len(t.components)) - t.mortonCodes = make([]uint32, len(t.components)) - - for i := range t.components { - component := &t.components[i] - aabb := component.AABB - center := aabb.Min.Add(aabb.Max).Scale(0.5) - leaves[i] = LeafNode{ - Entity: component.Entity, - Bounds: *aabb, - } - t.mortonCodes[i] = t.morton2D(center.X, center.Y) - } - t.components = t.components[:0] - - // 2. Сортируем листья по Morton code - indices := make([]int, len(leaves)) - for i := range indices { - indices[i] = i - } - slices.SortFunc(indices, func(a, b int) int { - return int(t.mortonCodes[a]) - int(t.mortonCodes[b]) - }) - - // Реорганизуем данные в соответствии с сортировкой - sortedLeaves := make([]LeafNode, len(leaves)) - sortedCodes := make([]uint32, len(leaves)) - for i, idx := range indices { - sortedLeaves[i] = leaves[idx] - sortedCodes[i] = t.mortonCodes[idx] - } - t.leafNodes = sortedLeaves - t.mortonCodes = sortedCodes - - // 3. Строим иерархию используя бинарное разбиение - if len(t.leafNodes) == 1 { - t.rootIndex = -1 // Помечаем как единственный лист - return - } - - // Рекурсивное построение через стек задач - type buildTask struct { - start, end int - parentPtr *int - } - - var stack []buildTask - rootTask := buildTask{0, len(t.leafNodes) - 1, &t.rootIndex} - stack = append(stack, rootTask) - - for len(stack) > 0 { - task := stack[len(stack)-1] - stack = stack[:len(stack)-1] - - start, end := task.start, task.end - if start == end { - *task.parentPtr = -start - 1 // Листовой узел - continue - } - - // Находим точку разделения по Morton codes - split := t.findSplit(start, end) - - // Создаем внутреннюю ноду - nodeIndex := len(t.innerNodes) - t.innerNodes = append(t.innerNodes, InnerNode{}) - *task.parentPtr = nodeIndex - - // Обрабатываем детей - stack = append(stack, buildTask{ - start: split + 1, - end: end, - parentPtr: &t.innerNodes[nodeIndex].Right, - }) - stack = append(stack, buildTask{ - start: start, - end: split, - parentPtr: &t.innerNodes[nodeIndex].Left, - }) - } - - // 4. Вычисляем AABB для всех нод - t.computeBounds(t.rootIndex) -} - -func (t *Tree2Du) findSplit(start, end int) int { - first := t.mortonCodes[start] - last := t.mortonCodes[end] - if first == last { - return (start + end) >> 1 - } - - commonPrefix := bits.LeadingZeros32(first ^ last) - split := start - step := end - start - - for { - step = (step + 1) >> 1 - newSplit := split + step - - if newSplit < end { - splitCode := t.mortonCodes[newSplit] - splitPrefix := bits.LeadingZeros32(first ^ splitCode) - if splitPrefix > commonPrefix { - split = newSplit - } - } - - if step <= 1 { - break - } - } - - return split -} - -func (t *Tree2Du) computeBounds(nodeIndex int) stdcomponents.AABB { - if nodeIndex < 0 { // Лист - leaf := t.leafNodes[-nodeIndex-1] - return leaf.Bounds - } - - node := &t.innerNodes[nodeIndex] - leftBounds := t.computeBounds(node.Left) - rightBounds := t.computeBounds(node.Right) - node.Bounds = t.mergeAABB(&leftBounds, &rightBounds) - - // Определяем ось разделения по Morton codes - leftCode := t.getMortonCode(node.Left) - rightCode := t.getMortonCode(node.Right) - diff := leftCode ^ rightCode - node.SplitAxis = bits.TrailingZeros32(diff) % 2 - - return node.Bounds -} - -func (t *Tree2Du) getMortonCode(nodeIndex int) uint32 { - if nodeIndex < 0 { - return t.mortonCodes[-nodeIndex-1] - } - // Для внутренних нод возвращаем код разделения - return t.mortonCodes[t.findSplitForNode(nodeIndex)] -} - -func (t *Tree2Du) findSplitForNode(nodeIndex int) int { - node := t.innerNodes[nodeIndex] - left := node.Left - for left >= 0 { - left = t.innerNodes[left].Left - } - return -left - 1 -} - -func (t *Tree2Du) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity { - if t.rootIndex == -1 { - if len(t.leafNodes) == 1 && t.aabbOverlap(&t.leafNodes[0].Bounds, aabb) { - result = append(result, t.leafNodes[0].Entity) - } - return result - } - - stack := make([]int, 0, 32) - stack = append(stack, t.rootIndex) - - for len(stack) > 0 { - nodeIndex := stack[len(stack)-1] - stack = stack[:len(stack)-1] - - if nodeIndex >= 0 { // Inner node - node := t.innerNodes[nodeIndex] - - if !t.aabbOverlap(&node.Bounds, aabb) { - continue - } - - // Оптимизация порядка обхода на основе оси разделения - if (node.SplitAxis == 0 && aabb.Min.X <= node.Bounds.Min.X) || - (node.SplitAxis == 1 && aabb.Min.Y <= node.Bounds.Min.Y) { - stack = append(stack, node.Right, node.Left) - } else { - stack = append(stack, node.Left, node.Right) - } - } else { // Leaf node - leafIndex := -nodeIndex - 1 - if t.aabbOverlap(&t.leafNodes[leafIndex].Bounds, aabb) { - result = append(result, t.leafNodes[leafIndex].Entity) - } - } - } - - return result -} - -// go:inline aabbOverlap checks if two AABB intersect -func (t *Tree2Du) aabbOverlap(a, b *stdcomponents.AABB) bool { - // Check for non-overlap conditions first (early exit) - if a.Max.X < b.Min.X || a.Min.X > b.Max.X { - return false - } - if a.Max.Y < b.Min.Y || a.Min.Y > b.Max.Y { - return false - } - return true -} - -// Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit -func (t *Tree2Du) expandBits2D(v uint32) uint32 { - v = (v * 0x00010001) & 0xFF0000FF - v = (v * 0x00000101) & 0x0F00F00F - v = (v * 0x00000011) & 0xC30C30C3 - v = (v * 0x00000005) & 0x24924924 - return v -} - -// 2D Morton code for centroids coordinates in [0,1] range -func (t *Tree2Du) morton2D(x, y float32) uint32 { - xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) - yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) - return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) -} - -// mergeAABB combines two AABB -func (t *Tree2Du) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { - return stdcomponents.AABB{ - Min: vectors.Vec2{ - X: min(a.Min.X, b.Min.X), - Y: min(a.Min.Y, b.Min.Y), - }, - Max: vectors.Vec2{ - X: max(a.Max.X, b.Max.X), - Y: max(a.Max.Y, b.Max.Y), - }, - } -} diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 4b75076e..dc6725ed 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -47,7 +47,7 @@ type CollisionDetectionBVHSystem struct { SpatialIndex *stdcomponents.SpatialIndexComponentManager AABB *stdcomponents.AABBComponentManager - trees []bvh.Tree2Du + trees []bvh.GenTree treesLookup map[stdcomponents.CollisionLayer]int collisionEvents []ecs.PagedArray[CollisionEvent] @@ -70,7 +70,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } // Init trees - s.trees = make([]bvh.Tree2Du, 0, 8) + s.trees = make([]bvh.GenTree, 0, 8) s.treesLookup = make(map[stdcomponents.CollisionLayer]int, 8) // Fill trees @@ -81,7 +81,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { treeId, exists := s.treesLookup[layer] if !exists { treeId = len(s.trees) - s.trees = append(s.trees, bvh.NewTree2Du(layer, s.AABB.Len())) + s.trees = append(s.trees, bvh.NewGenTree(layer, s.AABB.Len())) s.treesLookup[layer] = treeId } From 53918e3b5968cde996182f2e27e3735a7ce461c8 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Mar 2025 00:18:30 +0300 Subject: [PATCH 079/196] remove deprecated --- pkg/bvh/tree.go | 294 -------------------------- stdsystems/collision-detection-bvh.go | 1 + 2 files changed, 1 insertion(+), 294 deletions(-) delete mode 100644 pkg/bvh/tree.go diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go deleted file mode 100644 index c64ec019..00000000 --- a/pkg/bvh/tree.go +++ /dev/null @@ -1,294 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package bvh - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" - "gomp/vectors" - "math" - "math/bits" - "slices" -) - -// Node represents a node in the BVH tree -type Node struct { - Left, Right int // Child indices, -1 for leaf - MortonCode uint32 - Entity ecs.Entity - Bounds stdcomponents.AABB -} - -func (n *Node) isLeaf() bool { - return n.Left == -1 && n.Right == -1 -} - -func NewTree2D(layer stdcomponents.CollisionLayer, prealloc int) Tree2D { - return Tree2D{ - layer: layer, - components: make([]treeComponent, 0, prealloc), - nodes: ecs.NewPagedArray[Node](), - } -} - -// Tree2D represents a BVH tree -type Tree2D struct { - layer stdcomponents.CollisionLayer - nodes ecs.PagedArray[Node] - components []treeComponent - rootIndex int -} - -func (t *Tree2D) Layer() stdcomponents.CollisionLayer { - return t.layer -} - -func (t *Tree2D) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity { - if t.rootIndex == -1 || t.nodes.Len() == 0 { // Handle empty tree - return result - } - - // Use stack-based traversal - stack := [32]int{} - stackPtr := 0 - stack[stackPtr] = t.rootIndex - stackPtr++ - - for stackPtr > 0 { - stackPtr-- - nodeIndex := stack[stackPtr] - node := t.nodes.Get(nodeIndex) - - // Early exit if no AABB overlap - bounds := &node.Bounds - if bounds.Max.X < aabb.Min.X || bounds.Min.X > aabb.Max.X { - continue - } - - if bounds.Max.Y < aabb.Min.Y || bounds.Min.Y > aabb.Max.Y { - continue - } - - if node.Left == -1 && node.Right == -1 { - // Check detailed collision with node.Entity - result = append(result, node.Entity) - } else { - // Push child indices (right and left) onto the stack. - stack[stackPtr] = node.Right - stack[stackPtr+1] = node.Left - stackPtr += 2 - } - } - - return result -} - -func (t *Tree2D) AddComponent(entity ecs.Entity, aabbs *stdcomponents.AABB) { - t.components = append(t.components, treeComponent{ - Entity: entity, - AABB: aabbs, - }) -} - -type TaskType int - -const ( - BuildTaskType TaskType = iota - MergeTaskType -) - -type Task struct { - Type TaskType - Start int - End int -} - -func (t *Tree2D) Build() { - t.nodes.Reset() - - // Create leaf nodes - leaves := make([]Node, len(t.components)) - for i := range t.components { - component := &t.components[i] - aabb := component.AABB - center := aabb.Min.Add(aabb.Max).Scale(0.5) - code := t.morton2D(center.X, center.Y) - leaves[i] = Node{ - Left: -1, - Right: -1, - MortonCode: code, - Entity: component.Entity, - Bounds: *aabb, - } - } - t.components = t.components[:0] - - // Sort leaf nodes by morton code - slices.SortFunc(leaves, func(a, b Node) int { - return int(a.MortonCode) - int(b.MortonCode) - }) - - for i := range leaves { - t.nodes.Append(leaves[i]) - } - - // Stack-based hierarchy construction - var resultStack []int - taskStack := []Task{{Type: BuildTaskType, Start: 0, End: len(leaves) - 1}} - - for len(taskStack) > 0 { - task := taskStack[len(taskStack)-1] - taskStack = taskStack[:len(taskStack)-1] - - switch task.Type { - case BuildTaskType: - start, end := task.Start, task.End - if start == end { - // Leaf node: push its index to result stack - resultStack = append(resultStack, start) - } else { - split := t.findSplit(start, end) - // Schedule MergeTask after processing children - taskStack = append(taskStack, Task{Type: MergeTaskType}) - // Process right child first (LIFO order) - taskStack = append(taskStack, Task{Type: BuildTaskType, Start: split + 1, End: end}) - // Process left child next - taskStack = append(taskStack, Task{Type: BuildTaskType, Start: start, End: split}) - } - case MergeTaskType: - // Pop right then left from result stack - right := resultStack[len(resultStack)-1] - resultStack = resultStack[:len(resultStack)-1] - left := resultStack[len(resultStack)-1] - resultStack = resultStack[:len(resultStack)-1] - - // Create parent node and append to t.nodes - leftBounds := &t.nodes.Get(left).Bounds - rightBounds := &t.nodes.Get(right).Bounds - parent := Node{ - Left: left, - Right: right, - Bounds: t.mergeAABB(leftBounds, rightBounds), - } - t.nodes.Append(parent) - // Push parent index to result stack - resultStack = append(resultStack, t.nodes.Len()-1) - } - } - - // After processing all tasks, resultStack holds root index - if len(resultStack) > 0 { - t.rootIndex = resultStack[0] - } -} - -// findSplit finds the position where the highest bit changes -func (t *Tree2D) findSplit(start, end int) int { - // Identical Morton sortedMortonCodes => split the range in the middle. - first := t.nodes.Get(start).MortonCode - last := t.nodes.Get(end).MortonCode - - if first == last { - return (start + end) >> 1 - } - - // Calculate the number of highest bits that are the same - // for all objects, using the count-leading-zeros intrinsic. - commonPrefix := bits.LeadingZeros32(first ^ last) - - // Use binary search to find where the next bit differs. - // Specifically, we are looking for the highest object that - // shares more than commonPrefix bits with the first one. - split := start - step := end - start - - for { - step = (step + 1) >> 1 // exponential decrease - newSplit := split + step // proposed new position - - if newSplit < end { - splitCode := t.nodes.Get(newSplit).MortonCode - splitPrefix := bits.LeadingZeros32(first ^ splitCode) - if splitPrefix > commonPrefix { - split = newSplit - } - } - - if step <= 1 { - break - } - } - - return split -} - -// mergeAABB combines two AABB -func (t *Tree2D) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { - return stdcomponents.AABB{ - Min: vectors.Vec2{ - X: min(a.Min.X, b.Min.X), - Y: min(a.Min.Y, b.Min.Y), - }, - Max: vectors.Vec2{ - X: max(a.Max.X, b.Max.X), - Y: max(a.Max.Y, b.Max.Y), - }, - } -} - -// go:inline aabbOverlap checks if two AABB intersect -func (t *Tree2D) aabbOverlap(a, b *stdcomponents.AABB) bool { - // Check for non-overlap conditions first (early exit) - if a.Max.X < b.Min.X || a.Min.X > b.Max.X { - return false - } - if a.Max.Y < b.Min.Y || a.Min.Y > b.Max.Y { - return false - } - return true -} - -// Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit -func (t *Tree2D) expandBits2D(v uint32) uint32 { - v = (v * 0x00010001) & 0xFF0000FF - v = (v * 0x00000101) & 0x0F00F00F - v = (v * 0x00000011) & 0xC30C30C3 - v = (v * 0x00000005) & 0x24924924 - return v -} - -// 2D Morton code for centroids coordinates in [0,1] range -func (t *Tree2D) morton2D(x, y float32) uint32 { - xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) - yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) - return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) -} - -// Expands a 10-bit integer into 30 bits by inserting 2 zeros after each bit -func expandBits3D(v uint32) uint32 { - v = (v * 0x00010001) & 0xFF0000FF - v = (v * 0x00000101) & 0x0F00F00F - v = (v * 0x00000011) & 0xC30C30C3 - v = (v * 0x00000005) & 0x49249249 - return v -} - -// 3D Morton code for coordinates in [0,1] range -func morton3D(x, y, z float32) uint32 { - xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) - yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) - zz := uint32(math.Min(math.Max(float64(z)*1024.0, 0.0), 1023.0)) - return expandBits3D(xx)*4 + expandBits3D(yy)*2 + expandBits3D(zz) -} diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index dc6725ed..e7d017b5 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -61,6 +61,7 @@ func (s *CollisionDetectionBVHSystem) Init() { s.collisionEvents[i] = ecs.NewPagedArray[CollisionEvent]() } } + func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { s.currentCollisions = make(map[CollisionPair]struct{}) defer s.processExitStates() From af70d934a9f0b8cf159bd44857e29e86076bc1e5 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Mar 2025 00:22:37 +0300 Subject: [PATCH 080/196] rename bvh tree --- pkg/bvh/{gen-tree.go => tree.go} | 56 +++++++++++++-------------- stdsystems/collision-detection-bvh.go | 4 +- 2 files changed, 30 insertions(+), 30 deletions(-) rename pkg/bvh/{gen-tree.go => tree.go} (80%) diff --git a/pkg/bvh/gen-tree.go b/pkg/bvh/tree.go similarity index 80% rename from pkg/bvh/gen-tree.go rename to pkg/bvh/tree.go index 713642a6..2233d628 100644 --- a/pkg/bvh/gen-tree.go +++ b/pkg/bvh/tree.go @@ -23,57 +23,57 @@ import ( "slices" ) -type genNode struct { +type node struct { childIndex int16 // if < 0 then points to leaf } -type genLeaf struct { +type leaf struct { id ecs.Entity } -type genTreeComponent struct { +type component struct { entity ecs.Entity aabb *stdcomponents.AABB code uint32 } -func NewGenTree(layer stdcomponents.CollisionLayer, prealloc int) GenTree { - return GenTree{ - nodes: make([]genNode, 0, prealloc), +func NewGenTree(layer stdcomponents.CollisionLayer, prealloc int) Tree { + return Tree{ + nodes: make([]node, 0, prealloc), aabbNodes: make([]stdcomponents.AABB, 0, prealloc), - leaves: make([]genLeaf, 0, prealloc), + leaves: make([]leaf, 0, prealloc), aabbLeaves: make([]*stdcomponents.AABB, 0, prealloc), codes: make([]uint32, 0, prealloc), - components: make([]genTreeComponent, 0, prealloc), + components: make([]component, 0, prealloc), layer: layer, } } -type GenTree struct { - nodes []genNode +type Tree struct { + nodes []node aabbNodes []stdcomponents.AABB - leaves []genLeaf + leaves []leaf aabbLeaves []*stdcomponents.AABB codes []uint32 - components []genTreeComponent + components []component layer stdcomponents.CollisionLayer } -func (t *GenTree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { +func (t *Tree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { center := aabb.Min.Add(aabb.Max).Scale(0.5) code := t.morton2D(center.X, center.Y) - t.components = append(t.components, genTreeComponent{ + t.components = append(t.components, component{ entity: entity, aabb: aabb, code: code, }) } -func (t *GenTree) Build() { +func (t *Tree) Build() { // Reset tree t.nodes = t.nodes[:0] t.aabbNodes = t.aabbNodes[:0] @@ -81,14 +81,14 @@ func (t *GenTree) Build() { t.aabbLeaves = t.aabbLeaves[:0] // Sort components by morton code - slices.SortFunc(t.components, func(a, b genTreeComponent) int { + slices.SortFunc(t.components, func(a, b component) int { return int(a.code) - int(b.code) }) // Add leaves for i := 0; i < len(t.components); i++ { component := &t.components[i] - t.leaves = append(t.leaves, genLeaf{ + t.leaves = append(t.leaves, leaf{ id: component.entity, }) t.aabbLeaves = append(t.aabbLeaves, component.aabb) @@ -97,13 +97,13 @@ func (t *GenTree) Build() { t.components = t.components[:0] // Add root node - t.nodes = append(t.nodes, genNode{-1}) + t.nodes = append(t.nodes, node{-1}) t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) t.buildH(0, 0, len(t.leaves)-1) } -func (t *GenTree) buildH(parentIndex int, start, end int) { +func (t *Tree) buildH(parentIndex int, start, end int) { if start == end { // Is a leaf t.nodes[parentIndex].childIndex = -int16(start) @@ -115,12 +115,12 @@ func (t *GenTree) buildH(parentIndex int, start, end int) { // Add left node leftIndex := len(t.nodes) - t.nodes = append(t.nodes, genNode{-1}) + t.nodes = append(t.nodes, node{-1}) t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) // Add right node rightIndex := len(t.nodes) - t.nodes = append(t.nodes, genNode{-1}) + t.nodes = append(t.nodes, node{-1}) t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) t.nodes[parentIndex].childIndex = int16(leftIndex) @@ -131,11 +131,11 @@ func (t *GenTree) buildH(parentIndex int, start, end int) { t.aabbNodes[parentIndex] = t.mergeAABB(&t.aabbNodes[leftIndex], &t.aabbNodes[rightIndex]) } -func (t *GenTree) Layer() stdcomponents.CollisionLayer { +func (t *Tree) Layer() stdcomponents.CollisionLayer { return t.layer } -func (t *GenTree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity { +func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity { if len(t.nodes) == 0 { // Handle empty tree return result } @@ -176,13 +176,13 @@ func (t *GenTree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Ent } // go:inline aabbOverlap checks if two AABB intersect -func (t *GenTree) aabbOverlap(a, b *stdcomponents.AABB) bool { +func (t *Tree) aabbOverlap(a, b *stdcomponents.AABB) bool { return a.Max.X >= b.Min.X && a.Min.X <= b.Max.X && a.Max.Y >= b.Min.Y && a.Min.Y <= b.Max.Y } // findSplit finds the position where the highest bit changes -func (t *GenTree) findSplit(start, end int) int { +func (t *Tree) findSplit(start, end int) int { // Identical Morton sortedMortonCodes => split the range in the middle. first := t.codes[start] last := t.codes[end] @@ -222,7 +222,7 @@ func (t *GenTree) findSplit(start, end int) int { } // mergeAABB combines two AABB -func (t *GenTree) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { +func (t *Tree) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { return stdcomponents.AABB{ Min: vectors.Vec2{ X: min(a.Min.X, b.Min.X), @@ -236,7 +236,7 @@ func (t *GenTree) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { } // Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit -func (t *GenTree) expandBits2D(v uint32) uint32 { +func (t *Tree) expandBits2D(v uint32) uint32 { v = (v * 0x00010001) & 0xFF0000FF v = (v * 0x00000101) & 0x0F00F00F v = (v * 0x00000011) & 0xC30C30C3 @@ -245,7 +245,7 @@ func (t *GenTree) expandBits2D(v uint32) uint32 { } // 2D Morton code for centroids coordinates in [0,1] range -func (t *GenTree) morton2D(x, y float32) uint32 { +func (t *Tree) morton2D(x, y float32) uint32 { xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index e7d017b5..0499de46 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -47,7 +47,7 @@ type CollisionDetectionBVHSystem struct { SpatialIndex *stdcomponents.SpatialIndexComponentManager AABB *stdcomponents.AABBComponentManager - trees []bvh.GenTree + trees []bvh.Tree treesLookup map[stdcomponents.CollisionLayer]int collisionEvents []ecs.PagedArray[CollisionEvent] @@ -71,7 +71,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } // Init trees - s.trees = make([]bvh.GenTree, 0, 8) + s.trees = make([]bvh.Tree, 0, 8) s.treesLookup = make(map[stdcomponents.CollisionLayer]int, 8) // Fill trees From d27aad4bb31828b2e8d46aa38ed1207d9ed59eeb Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Mar 2025 01:16:18 +0300 Subject: [PATCH 081/196] small updates --- examples/new-api/systems/render-assterodd.go | 96 +++++++++++--------- pkg/bvh/tree.go | 23 +++-- stdsystems/collision-detection-bvh.go | 2 +- stdsystems/debug.go | 17 ++-- 4 files changed, 74 insertions(+), 64 deletions(-) diff --git a/examples/new-api/systems/render-assterodd.go b/examples/new-api/systems/render-assterodd.go index 27c5a14b..d4e2ca1c 100644 --- a/examples/new-api/systems/render-assterodd.go +++ b/examples/new-api/systems/render-assterodd.go @@ -26,6 +26,8 @@ import ( "time" ) +const debug = false + func NewRenderAssteroddSystem() RenderAssteroddSystem { return RenderAssteroddSystem{ instanceData: make([]stdcomponents.RLTexturePro, 0, 8192), @@ -109,34 +111,36 @@ func (s *RenderAssteroddSystem) render() { // ========== // DEBUG // ========== - //rl.BeginMode2D(s.camera) - //s.BoxColliders.EachEntity(func(e ecs.Entity) bool { - // col := s.BoxColliders.Get(e) - // scale := s.Scales.Get(e) - // pos := s.Positions.Get(e) - // rot := s.Rotations.Get(e) - // - // rl.DrawRectanglePro(rl.Rectangle{ - // X: pos.XY.X, - // Y: pos.XY.Y, - // Width: col.WH.X * scale.XY.X, - // Height: col.WH.Y * scale.XY.Y, - // }, rl.Vector2{ - // X: col.Offset.X * scale.XY.X, - // Y: col.Offset.Y * scale.XY.Y, - // }, float32(rot.Degrees()), rl.DarkGreen) - // return true - //}) - //s.CircleColliders.EachEntity(func(e ecs.Entity) bool { - // col := s.CircleColliders.Get(e) - // scale := s.Scales.Get(e) - // pos := s.Positions.Get(e) - // - // posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) - // rl.DrawCircle(int32(posWithOffset.X), int32(posWithOffset.Y), col.Radius*scale.XY.X, rl.DarkGreen) - // return true - //}) - //rl.EndMode2D() + if debug { + rl.BeginMode2D(s.camera) + s.BoxColliders.EachEntity(func(e ecs.Entity) bool { + col := s.BoxColliders.Get(e) + scale := s.Scales.Get(e) + pos := s.Positions.Get(e) + rot := s.Rotations.Get(e) + + rl.DrawRectanglePro(rl.Rectangle{ + X: pos.XY.X, + Y: pos.XY.Y, + Width: col.WH.X * scale.XY.X, + Height: col.WH.Y * scale.XY.Y, + }, rl.Vector2{ + X: col.Offset.X * scale.XY.X, + Y: col.Offset.Y * scale.XY.Y, + }, float32(rot.Degrees()), rl.DarkGreen) + return true + }) + s.CircleColliders.EachEntity(func(e ecs.Entity) bool { + col := s.CircleColliders.Get(e) + scale := s.Scales.Get(e) + pos := s.Positions.Get(e) + + posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) + rl.DrawCircle(int32(posWithOffset.X), int32(posWithOffset.Y), col.Radius*scale.XY.X, rl.DarkGreen) + return true + }) + rl.EndMode2D() + } // Extract and sort entities if cap(s.renderList) < s.Renderables.Len() { @@ -194,23 +198,25 @@ func (s *RenderAssteroddSystem) render() { // ========== // DEBUG // ========== - //rl.BeginMode2D(s.camera) - //s.AABBs.EachEntity(func(e ecs.Entity) bool { - // aabb := s.AABBs.Get(e) - // rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), rl.Green) - // return true - //}) - //s.Collisions.EachEntity(func(entity ecs.Entity) bool { - // pos := s.Positions.Get(entity) - // rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) - // return true - //}) - //s.Renderables.EachEntity(func(e ecs.Entity) bool { - // position := s.Positions.Get(e) - // rl.DrawRectangle(int32(position.XY.X-2), int32(position.XY.Y-2), 4, 4, rl.Red) - // return true - //}) - //rl.EndMode2D() + if debug { + rl.BeginMode2D(s.camera) + s.AABBs.EachEntity(func(e ecs.Entity) bool { + aabb := s.AABBs.Get(e) + rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), rl.Green) + return true + }) + s.Collisions.EachEntity(func(entity ecs.Entity) bool { + pos := s.Positions.Get(entity) + rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) + return true + }) + s.Renderables.EachEntity(func(e ecs.Entity) bool { + position := s.Positions.Get(e) + rl.DrawRectangle(int32(position.XY.X-2), int32(position.XY.Y-2), 4, 4, rl.Red) + return true + }) + rl.EndMode2D() + } } func (s *RenderAssteroddSystem) submitBatch(texID int, data []stdcomponents.RLTexturePro) { diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 2233d628..11582f44 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -24,7 +24,7 @@ import ( ) type node struct { - childIndex int16 // if < 0 then points to leaf + childIndex int32 // if < 0 then points to leaf } type leaf struct { @@ -106,7 +106,7 @@ func (t *Tree) Build() { func (t *Tree) buildH(parentIndex int, start, end int) { if start == end { // Is a leaf - t.nodes[parentIndex].childIndex = -int16(start) + t.nodes[parentIndex].childIndex = -int32(start) t.aabbNodes[parentIndex] = *t.aabbLeaves[start] return } @@ -123,7 +123,7 @@ func (t *Tree) buildH(parentIndex int, start, end int) { t.nodes = append(t.nodes, node{-1}) t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) - t.nodes[parentIndex].childIndex = int16(leftIndex) + t.nodes[parentIndex].childIndex = int32(leftIndex) t.buildH(leftIndex, start, split) t.buildH(rightIndex, split+1, end) @@ -237,19 +237,18 @@ func (t *Tree) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { // Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit func (t *Tree) expandBits2D(v uint32) uint32 { - v = (v * 0x00010001) & 0xFF0000FF - v = (v * 0x00000101) & 0x0F00F00F - v = (v * 0x00000011) & 0xC30C30C3 - v = (v * 0x00000005) & 0x24924924 + v = (v | (v << 16)) & 0x030000FF + v = (v | (v << 8)) & 0x0300F00F + v = (v | (v << 4)) & 0x030C30C3 + v = (v | (v << 2)) & 0x09249249 return v } +const mortonPrecision = 1 << 16 + // 2D Morton code for centroids coordinates in [0,1] range func (t *Tree) morton2D(x, y float32) uint32 { - xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) - yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) + xx := uint32(math.Min(math.Max(float64(x)*mortonPrecision, 0.0), mortonPrecision-1)) + yy := uint32(math.Min(math.Max(float64(y)*mortonPrecision, 0.0), mortonPrecision-1)) return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) } - -const a = 0xFF0000FF -const b = 0x030000FF diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 0499de46..d0bbc118 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -25,7 +25,7 @@ import ( "time" ) -var maxNumWorkers = runtime.NumCPU() - 2 +var maxNumWorkers = runtime.NumCPU() - 1 func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { return CollisionDetectionBVHSystem{ diff --git a/stdsystems/debug.go b/stdsystems/debug.go index 1137ddf3..e7b0610d 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -8,15 +8,17 @@ package stdsystems import ( "fmt" - //"github.com/felixge/fgprof" + "github.com/felixge/fgprof" rl "github.com/gen2brain/raylib-go/raylib" "log" - //"net/http" + "net/http" _ "net/http/pprof" "os" "runtime/pprof" ) +const gpprof = false + func NewDebugSystem() DebugSystem { return DebugSystem{} } @@ -26,10 +28,13 @@ type DebugSystem struct { } func (s *DebugSystem) Init() { - //http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler()) - //go func() { - // log.Println(http.ListenAndServe(":6060", nil)) - //}() + if gpprof { + http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler()) + go func() { + log.Println(http.ListenAndServe(":6060", nil)) + }() + } + } func (s *DebugSystem) Run() { if rl.IsKeyPressed(rl.KeyF9) { From 0a03a29fb07cb95520f674b08665171e324361b9 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Mar 2025 15:49:33 +0300 Subject: [PATCH 082/196] refactor bvh tree build function - x9 performance --- pkg/bvh/tree.go | 96 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 11582f44..9e1959fd 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -79,6 +79,7 @@ func (t *Tree) Build() { t.aabbNodes = t.aabbNodes[:0] t.leaves = t.leaves[:0] t.aabbLeaves = t.aabbLeaves[:0] + t.codes = t.codes[:0] // Sort components by morton code slices.SortFunc(t.components, func(a, b component) int { @@ -96,39 +97,86 @@ func (t *Tree) Build() { } t.components = t.components[:0] - // Add root node - t.nodes = append(t.nodes, node{-1}) - t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) - - t.buildH(0, 0, len(t.leaves)-1) -} - -func (t *Tree) buildH(parentIndex int, start, end int) { - if start == end { - // Is a leaf - t.nodes[parentIndex].childIndex = -int32(start) - t.aabbNodes[parentIndex] = *t.aabbLeaves[start] + if len(t.leaves) == 0 { + // No leaves, reset nodes and return + t.nodes = t.nodes[:0] + t.aabbNodes = t.aabbNodes[:0] return } - split := t.findSplit(start, end) - - // Add left node - leftIndex := len(t.nodes) + // Add root node t.nodes = append(t.nodes, node{-1}) t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) - // Add right node - rightIndex := len(t.nodes) - t.nodes = append(t.nodes, node{-1}) - t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) + type buildTask struct { + parentIndex int + start int + end int + childrenCreated bool + } - t.nodes[parentIndex].childIndex = int32(leftIndex) + stack := []buildTask{ + {parentIndex: 0, start: 0, end: len(t.leaves) - 1, childrenCreated: false}, + } - t.buildH(leftIndex, start, split) - t.buildH(rightIndex, split+1, end) + for len(stack) > 0 { + // Pop the last task + task := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + if !task.childrenCreated { + if task.start == task.end { + // Leaf node + t.nodes[task.parentIndex].childIndex = -int32(task.start) + t.aabbNodes[task.parentIndex] = *t.aabbLeaves[task.start] + continue + } - t.aabbNodes[parentIndex] = t.mergeAABB(&t.aabbNodes[leftIndex], &t.aabbNodes[rightIndex]) + split := t.findSplit(task.start, task.end) + + // Create left and right nodes + leftIndex := len(t.nodes) + t.nodes = append(t.nodes, node{-1}, node{-1}) // append left and right nodes + t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}, stdcomponents.AABB{}) + + // Set parent's childIndex to leftIndex + t.nodes[task.parentIndex].childIndex = int32(leftIndex) + + // Push parent task back with childrenCreated=true + stack = append(stack, buildTask{ + parentIndex: task.parentIndex, + start: task.start, + end: task.end, + childrenCreated: true, + }) + + // Push right child task (split+1 to end) + stack = append(stack, buildTask{ + parentIndex: leftIndex + 1, + start: split + 1, + end: task.end, + childrenCreated: false, + }) + + // Push left child task (start to split) + stack = append(stack, buildTask{ + parentIndex: leftIndex, + start: task.start, + end: split, + childrenCreated: false, + }) + } else { + // Merge children's AABBs into parent + leftChildIndex := int(t.nodes[task.parentIndex].childIndex) + rightChildIndex := leftChildIndex + 1 + + leftAABB := &t.aabbNodes[leftChildIndex] + rightAABB := &t.aabbNodes[rightChildIndex] + + merged := t.mergeAABB(leftAABB, rightAABB) + t.aabbNodes[task.parentIndex] = merged + } + } } func (t *Tree) Layer() stdcomponents.CollisionLayer { From abe69dd50eee3ba29b3606521b9d7dbb6b9973f1 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Mar 2025 16:07:14 +0300 Subject: [PATCH 083/196] refactor bvh tree - use pagedArray for nodes --- pkg/bvh/tree.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 9e1959fd..5b9a4dab 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -39,7 +39,7 @@ type component struct { func NewGenTree(layer stdcomponents.CollisionLayer, prealloc int) Tree { return Tree{ - nodes: make([]node, 0, prealloc), + nodes: ecs.NewPagedArray[node](), aabbNodes: make([]stdcomponents.AABB, 0, prealloc), leaves: make([]leaf, 0, prealloc), aabbLeaves: make([]*stdcomponents.AABB, 0, prealloc), @@ -50,7 +50,7 @@ func NewGenTree(layer stdcomponents.CollisionLayer, prealloc int) Tree { } type Tree struct { - nodes []node + nodes ecs.PagedArray[node] aabbNodes []stdcomponents.AABB leaves []leaf @@ -75,7 +75,7 @@ func (t *Tree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { func (t *Tree) Build() { // Reset tree - t.nodes = t.nodes[:0] + t.nodes.Reset() t.aabbNodes = t.aabbNodes[:0] t.leaves = t.leaves[:0] t.aabbLeaves = t.aabbLeaves[:0] @@ -99,13 +99,13 @@ func (t *Tree) Build() { if len(t.leaves) == 0 { // No leaves, reset nodes and return - t.nodes = t.nodes[:0] + t.nodes.Reset() t.aabbNodes = t.aabbNodes[:0] return } // Add root node - t.nodes = append(t.nodes, node{-1}) + t.nodes.Append(node{-1}) t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) type buildTask struct { @@ -127,7 +127,7 @@ func (t *Tree) Build() { if !task.childrenCreated { if task.start == task.end { // Leaf node - t.nodes[task.parentIndex].childIndex = -int32(task.start) + t.nodes.Get(task.parentIndex).childIndex = -int32(task.start) t.aabbNodes[task.parentIndex] = *t.aabbLeaves[task.start] continue } @@ -135,12 +135,13 @@ func (t *Tree) Build() { split := t.findSplit(task.start, task.end) // Create left and right nodes - leftIndex := len(t.nodes) - t.nodes = append(t.nodes, node{-1}, node{-1}) // append left and right nodes + leftIndex := t.nodes.Len() + t.nodes.Append(node{-1}) + t.nodes.Append(node{-1}) t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}, stdcomponents.AABB{}) // Set parent's childIndex to leftIndex - t.nodes[task.parentIndex].childIndex = int32(leftIndex) + t.nodes.Get(task.parentIndex).childIndex = int32(leftIndex) // Push parent task back with childrenCreated=true stack = append(stack, buildTask{ @@ -167,7 +168,7 @@ func (t *Tree) Build() { }) } else { // Merge children's AABBs into parent - leftChildIndex := int(t.nodes[task.parentIndex].childIndex) + leftChildIndex := int(t.nodes.Get(task.parentIndex).childIndex) rightChildIndex := leftChildIndex + 1 leftAABB := &t.aabbNodes[leftChildIndex] @@ -184,7 +185,7 @@ func (t *Tree) Layer() stdcomponents.CollisionLayer { } func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity { - if len(t.nodes) == 0 { // Handle empty tree + if t.nodes.Len() == 0 { // Handle empty tree return result } @@ -206,7 +207,7 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity continue } - node := &t.nodes[nodeIndex] + node := t.nodes.Get(nodeIndex) if node.childIndex <= 0 { // Is a leaf index := -node.childIndex From 383adeecdb43ab5e864f814fb894c6af8f6fc9dd Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Mar 2025 17:10:33 +0300 Subject: [PATCH 084/196] refactor bvh tree - from slices to pagedArrays --- pkg/bvh/tree.go | 98 +++++++++++++-------------- stdsystems/collision-detection-bvh.go | 2 +- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 5b9a4dab..e5b3a7c2 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -37,36 +37,32 @@ type component struct { code uint32 } -func NewGenTree(layer stdcomponents.CollisionLayer, prealloc int) Tree { +func NewTree(layer stdcomponents.CollisionLayer) Tree { return Tree{ nodes: ecs.NewPagedArray[node](), - aabbNodes: make([]stdcomponents.AABB, 0, prealloc), - leaves: make([]leaf, 0, prealloc), - aabbLeaves: make([]*stdcomponents.AABB, 0, prealloc), - codes: make([]uint32, 0, prealloc), - components: make([]component, 0, prealloc), + aabbNodes: ecs.NewPagedArray[stdcomponents.AABB](), + leaves: ecs.NewPagedArray[leaf](), + aabbLeaves: ecs.NewPagedArray[*stdcomponents.AABB](), + codes: ecs.NewPagedArray[uint32](), + components: ecs.NewPagedArray[component](), layer: layer, } } type Tree struct { - nodes ecs.PagedArray[node] - aabbNodes []stdcomponents.AABB - - leaves []leaf - aabbLeaves []*stdcomponents.AABB - - codes []uint32 - - components []component - - layer stdcomponents.CollisionLayer + nodes ecs.PagedArray[node] + aabbNodes ecs.PagedArray[stdcomponents.AABB] + leaves ecs.PagedArray[leaf] + aabbLeaves ecs.PagedArray[*stdcomponents.AABB] + codes ecs.PagedArray[uint32] + components ecs.PagedArray[component] + layer stdcomponents.CollisionLayer } func (t *Tree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { center := aabb.Min.Add(aabb.Max).Scale(0.5) code := t.morton2D(center.X, center.Y) - t.components = append(t.components, component{ + t.components.Append(component{ entity: entity, aabb: aabb, code: code, @@ -76,37 +72,36 @@ func (t *Tree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { func (t *Tree) Build() { // Reset tree t.nodes.Reset() - t.aabbNodes = t.aabbNodes[:0] - t.leaves = t.leaves[:0] - t.aabbLeaves = t.aabbLeaves[:0] - t.codes = t.codes[:0] - - // Sort components by morton code - slices.SortFunc(t.components, func(a, b component) int { + t.aabbNodes.Reset() + t.leaves.Reset() + t.aabbLeaves.Reset() + t.codes.Reset() + + // Extract and sort components by morton code + var componentsSlice = make([]component, 0, t.components.Len()) + for i := 0; i < t.components.Len(); i++ { + componentsSlice = append(componentsSlice, t.components.GetValue(i)) + } + slices.SortFunc(componentsSlice, func(a, b component) int { return int(a.code) - int(b.code) }) // Add leaves - for i := 0; i < len(t.components); i++ { - component := &t.components[i] - t.leaves = append(t.leaves, leaf{ - id: component.entity, - }) - t.aabbLeaves = append(t.aabbLeaves, component.aabb) - t.codes = append(t.codes, component.code) + for i := range componentsSlice { + component := &componentsSlice[i] + t.leaves.Append(leaf{id: component.entity}) + t.aabbLeaves.Append(component.aabb) + t.codes.Append(component.code) } - t.components = t.components[:0] + t.components.Reset() - if len(t.leaves) == 0 { - // No leaves, reset nodes and return - t.nodes.Reset() - t.aabbNodes = t.aabbNodes[:0] + if t.leaves.Len() == 0 { return } // Add root node t.nodes.Append(node{-1}) - t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}) + t.aabbNodes.Append(stdcomponents.AABB{}) type buildTask struct { parentIndex int @@ -116,7 +111,7 @@ func (t *Tree) Build() { } stack := []buildTask{ - {parentIndex: 0, start: 0, end: len(t.leaves) - 1, childrenCreated: false}, + {parentIndex: 0, start: 0, end: t.leaves.Len() - 1, childrenCreated: false}, } for len(stack) > 0 { @@ -128,7 +123,7 @@ func (t *Tree) Build() { if task.start == task.end { // Leaf node t.nodes.Get(task.parentIndex).childIndex = -int32(task.start) - t.aabbNodes[task.parentIndex] = *t.aabbLeaves[task.start] + t.aabbNodes.Set(task.parentIndex, *t.aabbLeaves.GetValue(task.start)) continue } @@ -138,7 +133,8 @@ func (t *Tree) Build() { leftIndex := t.nodes.Len() t.nodes.Append(node{-1}) t.nodes.Append(node{-1}) - t.aabbNodes = append(t.aabbNodes, stdcomponents.AABB{}, stdcomponents.AABB{}) + t.aabbNodes.Append(stdcomponents.AABB{}) + t.aabbNodes.Append(stdcomponents.AABB{}) // Set parent's childIndex to leftIndex t.nodes.Get(task.parentIndex).childIndex = int32(leftIndex) @@ -171,11 +167,11 @@ func (t *Tree) Build() { leftChildIndex := int(t.nodes.Get(task.parentIndex).childIndex) rightChildIndex := leftChildIndex + 1 - leftAABB := &t.aabbNodes[leftChildIndex] - rightAABB := &t.aabbNodes[rightChildIndex] + leftAABB := t.aabbNodes.Get(leftChildIndex) + rightAABB := t.aabbNodes.Get(rightChildIndex) merged := t.mergeAABB(leftAABB, rightAABB) - t.aabbNodes[task.parentIndex] = merged + t.aabbNodes.Set(task.parentIndex, merged) } } } @@ -199,7 +195,7 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity for stackPtr > 0 { stackPtr-- nodeIndex := stack[stackPtr] - a := &t.aabbNodes[nodeIndex] + a := t.aabbNodes.Get(nodeIndex) b := aabb // Early exit if no AABB overlap @@ -210,8 +206,8 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity node := t.nodes.Get(nodeIndex) if node.childIndex <= 0 { // Is a leaf - index := -node.childIndex - result = append(result, t.leaves[index].id) + index := -int(node.childIndex) + result = append(result, t.leaves.Get(index).id) continue } @@ -233,8 +229,8 @@ func (t *Tree) aabbOverlap(a, b *stdcomponents.AABB) bool { // findSplit finds the position where the highest bit changes func (t *Tree) findSplit(start, end int) int { // Identical Morton sortedMortonCodes => split the range in the middle. - first := t.codes[start] - last := t.codes[end] + first := t.codes.GetValue(start) + last := t.codes.GetValue(end) if first == last { return (start + end) >> 1 @@ -255,7 +251,7 @@ func (t *Tree) findSplit(start, end int) int { newSplit := split + step // proposed new position if newSplit < end { - splitCode := t.codes[newSplit] + splitCode := t.codes.GetValue(newSplit) splitPrefix := bits.LeadingZeros32(first ^ splitCode) if splitPrefix > commonPrefix { split = newSplit @@ -284,7 +280,7 @@ func (t *Tree) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { } } -// Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit +// Expands a 16-bit integer into 32 bits by inserting 1 zero after each bit func (t *Tree) expandBits2D(v uint32) uint32 { v = (v | (v << 16)) & 0x030000FF v = (v | (v << 8)) & 0x0300F00F diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index d0bbc118..692ac929 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -82,7 +82,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { treeId, exists := s.treesLookup[layer] if !exists { treeId = len(s.trees) - s.trees = append(s.trees, bvh.NewGenTree(layer, s.AABB.Len())) + s.trees = append(s.trees, bvh.NewTree(layer)) s.treesLookup[layer] = treeId } From 03cbe5cd2eebfb72910bf16d74b433bcd1406048 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 30 Mar 2025 03:01:07 +0300 Subject: [PATCH 085/196] feat collision sleep system --- examples/new-api/entities/bullet.go | 5 +- examples/new-api/instances/component-list.go | 90 +++++++++---------- examples/new-api/systems/damping.go | 6 ++ examples/new-api/systems/render-assterodd.go | 91 ++++++++++++-------- internal/sdl3-cgo/sdl3cgo-game.go | 1 + stdcomponents/colliders.go | 44 ++++++---- stdcomponents/ids.go | 1 + stdsystems/collider.go | 49 ++++++++--- stdsystems/collision-detection-bvh.go | 30 ++++--- stdsystems/sprite.go | 8 +- 10 files changed, 199 insertions(+), 126 deletions(-) diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 242ca41b..30ae635c 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -68,8 +68,9 @@ func CreateBullet( X: 0, Y: 0, }, - Layer: config.BulletCollisionLayer, - Mask: 1< -0.1 { + velocity.X = 0 + } + if velocity.Y < 0.1 && velocity.Y > -0.1 { + velocity.Y = 0 + } } return true }) diff --git a/examples/new-api/systems/render-assterodd.go b/examples/new-api/systems/render-assterodd.go index d4e2ca1c..b75d90ce 100644 --- a/examples/new-api/systems/render-assterodd.go +++ b/examples/new-api/systems/render-assterodd.go @@ -26,8 +26,6 @@ import ( "time" ) -const debug = false - func NewRenderAssteroddSystem() RenderAssteroddSystem { return RenderAssteroddSystem{ instanceData: make([]stdcomponents.RLTexturePro, 0, 8192), @@ -35,32 +33,34 @@ func NewRenderAssteroddSystem() RenderAssteroddSystem { } type RenderAssteroddSystem struct { - EntityManager *ecs.EntityManager - RlTexturePros *stdcomponents.RLTextureProComponentManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - AnimationPlayers *stdcomponents.AnimationPlayerComponentManager - Tints *stdcomponents.TintComponentManager - Flips *stdcomponents.FlipComponentManager - Renderables *stdcomponents.RenderableComponentManager - AnimationStates *stdcomponents.AnimationStateComponentManager - Sprites *stdcomponents.SpriteComponentManager - SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - CircleColliders *stdcomponents.CircleColliderComponentManager - AABBs *stdcomponents.AABBComponentManager - Collisions *stdcomponents.CollisionComponentManager - renderList []renderEntry - instanceData []stdcomponents.RLTexturePro - camera rl.Camera2D - SceneManager *components.AsteroidSceneManagerComponentManager + EntityManager *ecs.EntityManager + RlTexturePros *stdcomponents.RLTextureProComponentManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + Tints *stdcomponents.TintComponentManager + Flips *stdcomponents.FlipComponentManager + Renderables *stdcomponents.RenderableComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + Sprites *stdcomponents.SpriteComponentManager + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + AABBs *stdcomponents.AABBComponentManager + Collisions *stdcomponents.CollisionComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + renderList []renderEntry + instanceData []stdcomponents.RLTexturePro + camera rl.Camera2D + SceneManager *components.AsteroidSceneManagerComponentManager monitorWidth int monitorHeight int Player *components.PlayerTagComponentManager + debug bool } func (s *RenderAssteroddSystem) Init() { @@ -78,6 +78,10 @@ func (s *RenderAssteroddSystem) Run(dt time.Duration) bool { return false } + if rl.IsKeyPressed(rl.KeyF12) { + s.debug = !s.debug + } + s.prepareRender(dt) rl.BeginDrawing() @@ -111,7 +115,7 @@ func (s *RenderAssteroddSystem) render() { // ========== // DEBUG // ========== - if debug { + if s.debug { rl.BeginMode2D(s.camera) s.BoxColliders.EachEntity(func(e ecs.Entity) bool { col := s.BoxColliders.Get(e) @@ -135,8 +139,14 @@ func (s *RenderAssteroddSystem) render() { scale := s.Scales.Get(e) pos := s.Positions.Get(e) + color := rl.DarkGreen + isSleeping := s.ColliderSleepStateComponentManager.Get(e) + if isSleeping != nil { + color = rl.Blue + } + posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) - rl.DrawCircle(int32(posWithOffset.X), int32(posWithOffset.Y), col.Radius*scale.XY.X, rl.DarkGreen) + rl.DrawCircle(int32(posWithOffset.X), int32(posWithOffset.Y), col.Radius*scale.XY.X, color) return true }) rl.EndMode2D() @@ -185,24 +195,29 @@ func (s *RenderAssteroddSystem) render() { entry := &s.renderList[i] if entry.TextureId != currentTex || len(s.instanceData) >= 8192 { if len(s.instanceData) > 0 { - s.submitBatch(currentTex, s.instanceData) + s.submitBatch(s.instanceData) s.instanceData = s.instanceData[:0] } currentTex = entry.TextureId } s.instanceData = append(s.instanceData, s.getInstanceData(entry.Entity)) } - s.submitBatch(currentTex, s.instanceData) // Submit last batch + s.submitBatch(s.instanceData) // Submit last batch s.renderList = s.renderList[:0] // ========== // DEBUG // ========== - if debug { + if s.debug { rl.BeginMode2D(s.camera) s.AABBs.EachEntity(func(e ecs.Entity) bool { aabb := s.AABBs.Get(e) - rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), rl.Green) + color := rl.Green + isSleeping := s.ColliderSleepStateComponentManager.Get(e) + if isSleeping != nil { + color = rl.Blue + } + rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), color) return true }) s.Collisions.EachEntity(func(entity ecs.Entity) bool { @@ -210,19 +225,21 @@ func (s *RenderAssteroddSystem) render() { rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) return true }) - s.Renderables.EachEntity(func(e ecs.Entity) bool { - position := s.Positions.Get(e) - rl.DrawRectangle(int32(position.XY.X-2), int32(position.XY.Y-2), 4, 4, rl.Red) - return true - }) rl.EndMode2D() } } -func (s *RenderAssteroddSystem) submitBatch(texID int, data []stdcomponents.RLTexturePro) { +func (s *RenderAssteroddSystem) submitBatch(data []stdcomponents.RLTexturePro) { rl.BeginMode2D(s.camera) - for i := range data { - rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) + if s.debug { + for i := range data { + rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) + rl.DrawRectangle(int32(data[i].Dest.X-2), int32(data[i].Dest.Y-2), 4, 4, rl.Red) + } + } else { + for i := range data { + rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) + } } rl.EndMode2D() } diff --git a/internal/sdl3-cgo/sdl3cgo-game.go b/internal/sdl3-cgo/sdl3cgo-game.go index a06353fb..606d1cf1 100644 --- a/internal/sdl3-cgo/sdl3cgo-game.go +++ b/internal/sdl3-cgo/sdl3cgo-game.go @@ -30,6 +30,7 @@ func main() { runtime.LockOSThread() defer runtime.UnlockOSThread() + sdl.SetHint(sdl.HintRenderDriver, "gpu") must(sdl.Init(sdl.InitVideo)) defer sdl.Quit() diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index f061dd62..7dbb0e4d 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -42,10 +42,11 @@ const ( ) type BoxCollider struct { - WH vectors.Vec2 - Offset vectors.Vec2 - Layer CollisionLayer - Mask CollisionMask + WH vectors.Vec2 + Offset vectors.Vec2 + Layer CollisionLayer + Mask CollisionMask + AllowSleep bool } func (c *BoxCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { @@ -80,10 +81,11 @@ func NewBoxColliderComponentManager() BoxColliderComponentManager { } type CircleCollider struct { - Radius float32 - Layer CollisionLayer - Mask CollisionMask - Offset vectors.Vec2 + Radius float32 + Layer CollisionLayer + Mask CollisionMask + Offset vectors.Vec2 + AllowSleep bool } func (c *CircleCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { @@ -103,10 +105,11 @@ func NewCircleColliderComponentManager() CircleColliderComponentManager { } type PolygonCollider struct { - Vertices []vectors.Vec2 - Layer CollisionLayer - Mask CollisionMask - Offset vectors.Vec2 + Vertices []vectors.Vec2 + Layer CollisionLayer + Mask CollisionMask + Offset vectors.Vec2 + AllowSleep bool } func (c *PolygonCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { @@ -133,10 +136,11 @@ func NewPolygonColliderComponentManager() PolygonColliderComponentManager { } type GenericCollider struct { - Shape ColliderShape - Layer CollisionLayer - Mask CollisionMask - Offset vectors.Vec2 + Shape ColliderShape + Layer CollisionLayer + Mask CollisionMask + Offset vectors.Vec2 + AllowSleep bool } type GenericColliderComponentManager = ecs.ComponentManager[GenericCollider] @@ -144,3 +148,11 @@ type GenericColliderComponentManager = ecs.ComponentManager[GenericCollider] func NewGenericColliderComponentManager() GenericColliderComponentManager { return ecs.NewComponentManager[GenericCollider](GenericColliderComponentId) } + +type ColliderSleepState struct{} + +type ColliderSleepStateComponentManager = ecs.ComponentManager[ColliderSleepState] + +func NewColliderSleepStateComponentManager() ColliderSleepStateComponentManager { + return ecs.NewComponentManager[ColliderSleepState](ColliderSleepStateComponentId) +} diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 063e8bd5..18f1be4c 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -32,6 +32,7 @@ const ( GenericColliderComponentId ColliderBoxComponentId ColliderCircleComponentId + ColliderSleepStateComponentId PolygonColliderComponentId CollisionComponentId SpatialIndexComponentId diff --git a/stdsystems/collider.go b/stdsystems/collider.go index ede59ca6..278caeef 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -27,19 +27,21 @@ func NewColliderSystem() ColliderSystem { } type ColliderSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Scales *stdcomponents.ScaleComponentManager - Rotations *stdcomponents.RotationComponentManager - GenericColliders *stdcomponents.GenericColliderComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - CircleColliders *stdcomponents.CircleColliderComponentManager - AABB *stdcomponents.AABBComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Scales *stdcomponents.ScaleComponentManager + Rotations *stdcomponents.RotationComponentManager + Velocities *stdcomponents.VelocityComponentManager + GenericColliders *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + AABB *stdcomponents.AABBComponentManager } func (s *ColliderSystem) Init() {} func (s *ColliderSystem) Run(dt time.Duration) { - s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { + s.BoxColliders.EachEntityParallel(func(entity ecs.Entity) bool { boxCollider := s.BoxColliders.Get(entity) genCollider := s.GenericColliders.Get(entity) @@ -51,6 +53,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider.Offset.X = boxCollider.Offset.X genCollider.Offset.Y = boxCollider.Offset.Y genCollider.Shape = stdcomponents.BoxColliderShape + genCollider.AllowSleep = boxCollider.AllowSleep position := s.Positions.Get(entity) scale := s.Scales.Get(entity) @@ -79,7 +82,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.CircleColliders.EachEntity(func(entity ecs.Entity) bool { + s.CircleColliders.EachEntityParallel(func(entity ecs.Entity) bool { circleCollider := s.CircleColliders.Get(entity) genCollider := s.GenericColliders.Get(entity) @@ -92,6 +95,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider.Offset.X = circleCollider.Offset.X genCollider.Offset.Y = circleCollider.Offset.Y genCollider.Shape = stdcomponents.CircleColliderShape + genCollider.AllowSleep = circleCollider.AllowSleep position := s.Positions.Get(entity) scale := s.Scales.Get(entity) @@ -107,5 +111,30 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) + + s.GenericColliders.EachEntityParallel(func(entity ecs.Entity) bool { + genCollider := s.GenericColliders.Get(entity) + + if genCollider.AllowSleep { + shouldSleep := true + velocity := s.Velocities.Get(entity) + if velocity != nil { + if velocity.Vec2().LengthSquared() != 0 { + shouldSleep = false + } + } + isSleeping := s.ColliderSleepStateComponentManager.Get(entity) + if shouldSleep { + if isSleeping == nil { + isSleeping = s.ColliderSleepStateComponentManager.Create(entity, stdcomponents.ColliderSleepState{}) + } + } else { + if isSleeping != nil { + s.ColliderSleepStateComponentManager.Remove(entity) + } + } + } + return true + }) } func (s *ColliderSystem) Destroy() {} diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 692ac929..3c1eeff7 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -35,17 +35,18 @@ func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { } type CollisionDetectionBVHSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - GenericCollider *stdcomponents.GenericColliderComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - CircleColliders *stdcomponents.CircleColliderComponentManager - PolygonColliders *stdcomponents.PolygonColliderComponentManager - Collisions *stdcomponents.CollisionComponentManager - SpatialIndex *stdcomponents.SpatialIndexComponentManager - AABB *stdcomponents.AABBComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + PolygonColliders *stdcomponents.PolygonColliderComponentManager + Collisions *stdcomponents.CollisionComponentManager + SpatialIndex *stdcomponents.SpatialIndexComponentManager + AABB *stdcomponents.AABBComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager trees []bvh.Tree treesLookup map[stdcomponents.CollisionLayer]int @@ -177,6 +178,13 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, workerId int) { colliderA := s.GenericCollider.Get(entityA) + if colliderA.AllowSleep { + isSleeping := s.ColliderSleepStateComponentManager.Get(entityA) + if isSleeping != nil { + return + } + } + aabb := s.AABB.Get(entityA) result := make([]ecs.Entity, 0, 64) diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index 5f676d23..febfdf2d 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -61,16 +61,12 @@ func (s *SpriteSystem) Run() { Y: sprite.Origin.Y * scale.XY.Y, }, Dest: rl.Rectangle{X: position.XY.X, Y: position.XY.Y, Width: sprite.Frame.Width, Height: sprite.Frame.Height}, // - Tint: stdcomponents.Tint{ - R: 255, - G: 255, - B: 255, - A: 255, - }, + Tint: sprite.Tint, }) } else { tr.Dest.Width = sprite.Frame.Width tr.Dest.Height = sprite.Frame.Height + tr.Tint = sprite.Tint } return true }) From 0b257f56b7c7bd7c772e6814003f4632aa3ec578 Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 30 Mar 2025 13:56:52 +0300 Subject: [PATCH 086/196] refactor(audio): implements audio in ecs-way All raylib calls now sits in audio-related systems. Added SoundEffect component and SpatialAudioSystem --- examples/new-api/components/audio.go | 38 +++++++ examples/new-api/components/ids.go | 1 + examples/new-api/entities/spaceship.go | 10 ++ examples/new-api/instances/component-list.go | 2 + examples/new-api/instances/system-list.go | 6 +- examples/new-api/scenes/assterodd-scene.go | 4 + examples/new-api/systems/asterodd.go | 2 + examples/new-api/systems/audio.go | 54 ++++++++-- examples/new-api/systems/collision-handler.go | 19 +++- examples/new-api/systems/spaceship-intents.go | 32 +++--- examples/new-api/systems/spatial-audio.go | 101 ++++++++++++++++++ 11 files changed, 242 insertions(+), 27 deletions(-) create mode 100644 examples/new-api/components/audio.go create mode 100644 examples/new-api/systems/spatial-audio.go diff --git a/examples/new-api/components/audio.go b/examples/new-api/components/audio.go new file mode 100644 index 00000000..8d417755 --- /dev/null +++ b/examples/new-api/components/audio.go @@ -0,0 +1,38 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" +) + +type SoundEffect struct { + Clip *rl.Sound + IsPlaying bool + IsLooping bool + // base is 1.0 + Volume float32 + // base is 1.0 + Pitch float32 + // base is 0.5. 1.0 is left, 0.0 is right + Pan float32 +} + +type SoundEffectsComponentManager = ecs.ComponentManager[SoundEffect] + +func NewSoundEffectsComponentManager() SoundEffectsComponentManager { + return ecs.NewComponentManager[SoundEffect](SoundEffectId) +} diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go index 443d0952..e6297596 100644 --- a/examples/new-api/components/ids.go +++ b/examples/new-api/components/ids.go @@ -29,4 +29,5 @@ const ( WeaponComponentId SpaceshipIntentComponentId AsteroidSceneManagerComponentId + SoundEffectId ) diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go index f21778c4..04c89c85 100644 --- a/examples/new-api/entities/spaceship.go +++ b/examples/new-api/entities/spaceship.go @@ -40,6 +40,7 @@ type CreateSpaceShipManagers struct { Hps *components.HpComponentManager Weapons *components.WeaponComponentManager SpaceshipIntents *components.SpaceshipIntentComponentManager + SoundEffects *components.SoundEffectsComponentManager } func CreateSpaceShip( @@ -48,6 +49,7 @@ func CreateSpaceShip( angle float64, ) ecs.Entity { spaceShip := props.EntityManager.Create() + props.Positions.Create(spaceShip, stdcomponents.Position{ XY: vectors.Vec2{ X: posX, @@ -107,6 +109,14 @@ func CreateSpaceShip( }) props.SpaceshipIntents.Create(spaceShip, components.SpaceshipIntent{}) + props.SoundEffects.Create(spaceShip, components.SoundEffect{ + Clip: assets.Audio.Get("fly_sound.wav"), + IsPlaying: false, + IsLooping: true, + Pitch: 1.0, + Volume: 1.0, + Pan: 0.5, + }) return spaceShip } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index e9808cde..052e75f4 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -54,6 +54,7 @@ type ComponentList struct { Weapon components.WeaponComponentManager SpaceshipIntent components.SpaceshipIntentComponentManager AsteroidSceneManager components.AsteroidSceneManagerComponentManager + SoundEffects components.SoundEffectsComponentManager } func NewComponentList() ComponentList { @@ -92,5 +93,6 @@ func NewComponentList() ComponentList { Weapon: components.NewWeaponComponentManager(), SpaceshipIntent: components.NewSpaceshipIntentComponentManager(), AsteroidSceneManager: components.NewAsteroidSceneManagerComponentManager(), + SoundEffects: components.NewSoundEffectsComponentManager(), } } diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index d433593b..c99c0de2 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -44,7 +44,8 @@ func NewSystemList() SystemList { RenderAssterodd: systems.NewRenderAssteroddSystem(), RenderBogdan: systems.NewRenderBogdanSystem(), - Audio: systems.NewAudioSystem(), + Audio: systems.NewAudioSystem(), + SpatialAudio: systems.NewSpatialAudioSystem(), DampingSystem: systems.NewDampingSystem(), AssteroddSystem: systems.NewAssteroddSystem(), @@ -79,7 +80,8 @@ type SystemList struct { RenderAssterodd systems.RenderAssteroddSystem RenderBogdan systems.RenderBogdanSystem - Audio systems.AudioSystem + Audio systems.AudioSystem + SpatialAudio systems.SpatialAudioSystem DampingSystem systems.DampingSystem AssteroddSystem systems.AssteroddSystem diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index 962c8da4..c1b9d833 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -98,11 +98,14 @@ func (s *AssteroddScene) Init() { s.World.Systems.Debug.Init() s.World.Systems.AssetLib.Init() s.World.Systems.Audio.Init() + s.World.Systems.SpatialAudio.Init() } func (s *AssteroddScene) Update(dt time.Duration) gomp.SceneId { s.World.Systems.ColliderSystem.Run(dt) s.World.Systems.AssteroddSystem.Run(dt) + s.World.Systems.Audio.Run(dt) + s.World.Systems.SpatialAudio.Run(dt) return AssteroddSceneId } @@ -165,6 +168,7 @@ func (s *AssteroddScene) Destroy() { s.World.Systems.AssetLib.Destroy() s.World.Systems.RenderAssterodd.Destroy() s.World.Systems.Audio.Destroy() + s.World.Systems.SpatialAudio.Destroy() } func (s *AssteroddScene) OnEnter() { diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index 1b8537f9..725492dd 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -47,6 +47,7 @@ type AssteroddSystem struct { Collisions *stdcomponents.CollisionComponentManager SceneManager *components.AsteroidSceneManagerComponentManager WallTags *components.WallTagComponentManager + SoundEffects *components.SoundEffectsComponentManager } func (s *AssteroddSystem) Init() { @@ -63,6 +64,7 @@ func (s *AssteroddSystem) Init() { Hps: s.Hps, Weapons: s.Weapons, SpaceshipIntents: s.SpaceshipIntents, + SoundEffects: s.SoundEffects, }, 300, 300, -44.9) entities.CreateSatellite(entities.CreateSatelliteManagers{ EntityManager: s.EntityManager, diff --git a/examples/new-api/systems/audio.go b/examples/new-api/systems/audio.go index 78016051..24411200 100644 --- a/examples/new-api/systems/audio.go +++ b/examples/new-api/systems/audio.go @@ -15,7 +15,6 @@ Thank you for your support! package systems import ( - "gomp/examples/new-api/assets" "gomp/examples/new-api/components" "gomp/pkg/ecs" "time" @@ -28,18 +27,59 @@ func NewAudioSystem() AudioSystem { } type AudioSystem struct { - EntityManager *ecs.EntityManager - SpaceshipIntents *components.SpaceshipIntentComponentManager + EntityManager *ecs.EntityManager + SoundEffects *components.SoundEffectsComponentManager } func (s *AudioSystem) Init() { rl.InitAudioDevice() +} + +func (s *AudioSystem) Run(dt time.Duration) { + s.SoundEffects.EachEntity(func(entity ecs.Entity) bool { + soundEffect := s.SoundEffects.Get(entity) + clip := soundEffect.Clip + + // check if clip is valid + if clip == nil || clip.FrameCount == 0 { + return true + } + + if !soundEffect.IsPlaying { + if rl.IsSoundPlaying(*clip) { + rl.StopSound(*clip) + return true + } else { + *clip = rl.LoadSoundAlias(*clip) + + rl.SetSoundVolume(*clip, soundEffect.Volume) + rl.SetSoundPitch(*clip, soundEffect.Pitch) + rl.SetSoundPan(*clip, soundEffect.Pan) + + rl.PlaySound(*clip) + soundEffect.IsPlaying = true + return true + } + } + + rl.SetSoundVolume(*clip, soundEffect.Volume) + rl.SetSoundPitch(*clip, soundEffect.Pitch) + rl.SetSoundPan(*clip, soundEffect.Pan) + + // check if sound is over + if !rl.IsSoundPlaying(*clip) && soundEffect.IsPlaying { + if soundEffect.IsLooping { + rl.PlaySound(*clip) + } else { + // sound is over, remove entity + s.EntityManager.Delete(entity) + // rl.UnloadSoundAlias(*clip) // TODO: this doesn't work https://github.com/gen2brain/raylib-go/issues/494 + } + } - assets.Audio.Load("damage_sound.wav") - assets.Audio.Load("fly_sound.wav") - assets.Audio.Load("gun_sound.wav") + return true + }) } -func (s *AudioSystem) Run(dt time.Duration) {} func (s *AudioSystem) Destroy() { rl.CloseAudioDevice() } diff --git a/examples/new-api/systems/collision-handler.go b/examples/new-api/systems/collision-handler.go index 61d46a4f..6332fd6e 100644 --- a/examples/new-api/systems/collision-handler.go +++ b/examples/new-api/systems/collision-handler.go @@ -15,7 +15,6 @@ Thank you for your support! package systems import ( - rl "github.com/gen2brain/raylib-go/raylib" "gomp/examples/new-api/assets" "gomp/examples/new-api/components" "gomp/pkg/ecs" @@ -45,6 +44,7 @@ type CollisionHandlerSystem struct { Sprites *stdcomponents.SpriteComponentManager BoxColliders *stdcomponents.BoxColliderComponentManager WallTags *components.WallTagComponentManager + SoundEffects *components.SoundEffectsComponentManager } func (s *CollisionHandlerSystem) Init() {} @@ -96,12 +96,23 @@ func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bo asteroidTag := s.AsteroidTags.Get(e2) if asteroidTag != nil { hp := s.Hps.Get(e1) - damageSound := assets.Audio.Get("damage_sound.wav") - rl.SetSoundPitch(*damageSound, float32(1.0+(float32(hp.MaxHp)-float32(hp.Hp))/float32(hp.MaxHp))) hp.Hp -= 1 - rl.PlaySound(*damageSound) + sfxEntity := s.EntityManager.Create() + + playerPos := s.Positions.Get(e1) + s.Positions.Create(sfxEntity, stdcomponents.Position{XY: playerPos.XY}) + + s.SoundEffects.Create(sfxEntity, components.SoundEffect{ + Clip: assets.Audio.Get("damage_sound.wav"), + Pitch: float32(1.0 + (float32(hp.MaxHp)-float32(hp.Hp))/float32(hp.MaxHp)), // higher pitch = less hp + IsPlaying: false, + IsLooping: false, + Volume: 1.0, + Pan: 0.5, + }) + return true } diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index bd98fd36..62a32e3a 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -23,8 +23,6 @@ import ( "gomp/vectors" "math" "time" - - rl "github.com/gen2brain/raylib-go/raylib" ) func NewSpaceshipIntentsSystem() SpaceshipIntentsSystem { @@ -45,6 +43,7 @@ type SpaceshipIntentsSystem struct { RigidBodies *stdcomponents.RigidBodyComponentManager Weapons *components.WeaponComponentManager Hps *components.HpComponentManager + SoundEffects *components.SoundEffectsComponentManager moveSpeed float32 } @@ -66,6 +65,7 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { pos := s.Positions.Get(entity) weapon := s.Weapons.Get(entity) hp := s.Hps.Get(entity) + flySfx := s.SoundEffects.Get(entity) if intent.RotateLeft { rot.Angle -= rotateSpeed * vectors.Radians(dtSec) @@ -94,17 +94,13 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { } } - flySound := assets.Audio.Get("fly_sound.wav") - flySoundIsPlaying := rl.IsSoundPlaying(*flySound) - absMoveSpeed := math.Abs(float64(s.moveSpeed)) + flySfx.Volume = float32(absMoveSpeed / float64(moveSpeedMax)) - rl.SetSoundVolume(*flySound, float32(absMoveSpeed/float64(moveSpeedMax))) - - if (intent.MoveUp || intent.MoveDown || intent.RotateLeft || intent.RotateRight || s.moveSpeed != 0) && !flySoundIsPlaying { - rl.PlaySound(*flySound) - } else if !(intent.MoveUp || intent.MoveDown || intent.RotateLeft || intent.RotateRight || s.moveSpeed != 0) && flySoundIsPlaying || hp.Hp == 0 { - rl.StopSound(*flySound) + if (intent.MoveUp || intent.MoveDown || intent.RotateLeft || intent.RotateRight || s.moveSpeed != 0) && !flySfx.IsPlaying { + flySfx.IsPlaying = true + } else if !(intent.MoveUp || intent.MoveDown || intent.RotateLeft || intent.RotateRight || s.moveSpeed != 0) && flySfx.IsPlaying || hp.Hp == 0 { + flySfx.IsPlaying = false } vel.Y = float32(math.Cos(rot.Angle+math.Pi)) * s.moveSpeed @@ -132,9 +128,17 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { }, pos.XY.X, pos.XY.Y, angle, bulletVelocityX, bulletVelocityY) } weapon.CooldownLeft = weapon.Cooldown - fireSoundAsset := assets.Audio.Get("gun_sound.wav") - fireSound := rl.LoadSoundAlias(*fireSoundAsset) - rl.PlaySound(fireSound) + + fireSoundEntity := s.EntityManager.Create() + + s.SoundEffects.Create(fireSoundEntity, components.SoundEffect{ + Clip: assets.Audio.Get("gun_sound.wav"), + IsPlaying: false, + IsLooping: false, + Pitch: 1.0, + Volume: 1.0, + Pan: 0.5, + }) } } else { weapon.CooldownLeft -= dt diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go new file mode 100644 index 00000000..419a3024 --- /dev/null +++ b/examples/new-api/systems/spatial-audio.go @@ -0,0 +1,101 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package systems + +import ( + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "math" + "time" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +func NewSpatialAudioSystem() SpatialAudioSystem { + return SpatialAudioSystem{} +} + +type SpatialAudioSystem struct { + EntityManager *ecs.EntityManager + SoundEffects *components.SoundEffectsComponentManager + Positions *stdcomponents.PositionComponentManager + Player *components.PlayerTagComponentManager +} + +func (s *SpatialAudioSystem) Init() { +} +func (s *SpatialAudioSystem) Run(dt time.Duration) { + var player ecs.Entity = 0 + + s.Player.EachEntity(func(entity ecs.Entity) bool { + player = entity + return false + }) + + if player == 0 { + return + } + + playerPos := s.Positions.Get(player) + + if playerPos == nil { + return + } + + s.SoundEffects.EachEntity(func(entity ecs.Entity) bool { + soundEffect := s.SoundEffects.Get(entity) + + clip := soundEffect.Clip + + if clip == nil { + return true + } + + if !soundEffect.IsPlaying { + return true + } + + position := s.Positions.Get(entity) + + if position == nil { + return true + } + + pan := s.CalculatePan(playerPos.XY, position.XY) + rl.SetSoundPan(*clip, pan) + + return true + }) +} +func (s *SpatialAudioSystem) Destroy() { +} + +func (s *SpatialAudioSystem) CalculatePan(listener vectors.Vec2, source vectors.Vec2) float32 { + dx := float64(source.X - listener.X) + dy := float64(source.Y - listener.Y) + distanceSq := dx*dx + dy*dy + + // Если источник и слушатель в одной точке + if distanceSq < 1e-9 { // Используем квадрат расстояния для оптимизации + return 0.5 + } + + distance := math.Sqrt(distanceSq) + pan := 0.5 - (dx / (2 * distance)) + + return float32(math.Max(0, math.Min(1, pan))) +} From 7e0a145e18b84335ced0a695bc2c510e47ad84e7 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 30 Mar 2025 15:20:42 +0300 Subject: [PATCH 087/196] update sdl3 purego and cgo internals --- internal/sdl3-cgo/sdl3cgo-game.go | 57 ++++++++++++++-------- internal/sdl3-pure/sdl-game.go | 79 +++++++++++++++++++++++-------- 2 files changed, 96 insertions(+), 40 deletions(-) diff --git a/internal/sdl3-cgo/sdl3cgo-game.go b/internal/sdl3-cgo/sdl3cgo-game.go index 606d1cf1..ccb1764b 100644 --- a/internal/sdl3-cgo/sdl3cgo-game.go +++ b/internal/sdl3-cgo/sdl3cgo-game.go @@ -1,32 +1,37 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - package main import "C" import ( "fmt" "github.com/jfreymuth/go-sdl3/sdl" + "log" + "os" "runtime" + "runtime/pprof" "time" ) const batchSize = 1 << 14 const rectCounter = 1 << 18 const framerate = 600 +const frameCount = 100 // Number of frames to average func main() { + f, err := os.Create("cpu.out") + if err != nil { + log.Fatal(err) + } + defer f.Close() + + err = pprof.StartCPUProfile(f) + if err != nil { + log.Fatal(err) + } + defer pprof.StopCPUProfile() + + fmt.Println("CPU Profile Started") + defer fmt.Println("CPU Profile Stopped") + runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -34,7 +39,7 @@ func main() { must(sdl.Init(sdl.InitVideo)) defer sdl.Quit() - w, r, e := sdl.CreateWindowAndRenderer("sld c-go", 2560, 1440, sdl.WindowResizable) + w, r, e := sdl.CreateWindowAndRenderer("sld c-go", 1280, 720, sdl.WindowResizable) must(e) defer w.Destroy() defer r.Destroy() @@ -46,7 +51,7 @@ func main() { } var dt time.Duration = time.Second - var fps float64 + var fps, avgFps float64 updatedAt := time.Now() rects := make([]sdl.FRect, rectCounter) @@ -54,15 +59,28 @@ func main() { rects[i] = sdl.FRect{X: 300, Y: 300, W: 10, H: 10} } + frameTimes := make([]float64, 0, frameCount) + Outer: for { if renderTicker != nil { <-renderTicker.C } - fps = (time.Second.Seconds() / dt.Seconds()) + fps = time.Second.Seconds() / dt.Seconds() dt = time.Since(updatedAt) updatedAt = time.Now() + frameTimes = append(frameTimes, fps) + if len(frameTimes) > frameCount { + frameTimes = frameTimes[1:] + } + + var totalFps float64 + for _, frameTime := range frameTimes { + totalFps += frameTime + } + avgFps = totalFps / float64(len(frameTimes)) + var event sdl.Event for sdl.PollEvent(&event) { switch event.Type() { @@ -84,10 +102,9 @@ Outer: must(r.FillRects(rects[i : i+batchSize])) } - //must(r.FillRects(rects)) - must(r.DebugText(10, 10, fmt.Sprintf("FPS %f", fps))) - must(r.DebugText(10, 20, fmt.Sprintf("dt %s", dt.String()))) + must(r.DebugText(10, 20, fmt.Sprintf("Avg FPS %f", avgFps))) + must(r.DebugText(10, 30, fmt.Sprintf("dt %s", dt.String()))) must(r.Present()) } diff --git a/internal/sdl3-pure/sdl-game.go b/internal/sdl3-pure/sdl-game.go index a94b662e..2cce6539 100644 --- a/internal/sdl3-pure/sdl-game.go +++ b/internal/sdl3-pure/sdl-game.go @@ -5,10 +5,16 @@ import ( "github.com/jupiterrider/purego-sdl3/sdl" "log" "os" + "runtime" "runtime/pprof" "time" ) +const batchSize = 1 << 14 +const rectCounter = 1 << 18 +const framerate = 600 +const frameCount = 100 // Number of frames to average + func main() { f, err := os.Create("cpu.out") if err != nil { @@ -25,34 +31,61 @@ func main() { fmt.Println("CPU Profile Started") defer fmt.Println("CPU Profile Stopped") - //if !sdl3-pure.SetHint(sdl3-pure.HintFramebufferAcceleration, "1") { - // panic(sdl3-pure.GetError()) - //} + runtime.LockOSThread() + defer runtime.UnlockOSThread() + sdl.SetHint(sdl.HintRenderDriver, "gpu") defer sdl.Quit() if !sdl.Init(sdl.InitVideo) { panic(sdl.GetError()) } - var window *sdl.Window - var renderer *sdl.Renderer + var w *sdl.Window + var r *sdl.Renderer - if !sdl.CreateWindowAndRenderer("Hello, World!", 1280, 720, sdl.WindowResizable, &window, &renderer) { + if !sdl.CreateWindowAndRenderer("Hello, World!", 1280, 720, sdl.WindowResizable, &w, &r) { panic(sdl.GetError()) } - defer sdl.DestroyRenderer(renderer) - defer sdl.DestroyWindow(window) + defer sdl.DestroyRenderer(r) + defer sdl.DestroyWindow(w) - sdl.SetRenderDrawColor(renderer, 100, 150, 200, 255) var dt time.Duration = time.Second - var fps int64 + var fps, avgFps float64 updatedAt := time.Now() + + var renderTicker *time.Ticker + if framerate > 0 { + renderTicker = time.NewTicker(time.Second / time.Duration(framerate)) + defer renderTicker.Stop() + } + + rects := make([]sdl.FRect, rectCounter) + for i := range rects { + rects[i] = sdl.FRect{X: 300, Y: 300, W: 10, H: 10} + } + + frameTimes := make([]float64, 0, frameCount) + Outer: for { - fps = int64(1 / dt.Seconds()) + if renderTicker != nil { + <-renderTicker.C + } + fps = time.Second.Seconds() / dt.Seconds() dt = time.Since(updatedAt) updatedAt = time.Now() + frameTimes = append(frameTimes, fps) + if len(frameTimes) > frameCount { + frameTimes = frameTimes[1:] + } + + var totalFps float64 + for _, frameTime := range frameTimes { + totalFps += frameTime + } + avgFps = totalFps / float64(len(frameTimes)) + var event sdl.Event for sdl.PollEvent(&event) { switch event.Type() { @@ -64,19 +97,25 @@ Outer: } } } - sdl.SetRenderDrawColor(renderer, 0, 0, 0, 255) - sdl.RenderClear(renderer) + sdl.SetRenderDrawColor(r, 0, 0, 0, 255) + sdl.RenderClear(r) - sdl.SetRenderDrawColor(renderer, 255, 255, 255, 255) - rect := sdl.FRect{300, 300, 200, 200} - for range 100_000 { - sdl.RenderFillRect(renderer, &rect) + sdl.SetRenderDrawColor(r, 255, 255, 255, 255) + for i := 0; i < len(rects); i += batchSize { + sdl.RenderFillRects(r, rects[i:i+batchSize]...) } - sdl.RenderDebugText(renderer, 10, 10, fmt.Sprintf("FPS %d", fps)) - sdl.RenderDebugText(renderer, 10, 20, fmt.Sprintf("dt %s", dt.String())) + sdl.RenderDebugText(r, 10, 10, fmt.Sprintf("FPS %f", fps)) + sdl.RenderDebugText(r, 10, 20, fmt.Sprintf("Avg FPS %f", avgFps)) + sdl.RenderDebugText(r, 10, 30, fmt.Sprintf("dt %s", dt.String())) - sdl.RenderPresent(renderer) + sdl.RenderPresent(r) + } +} +func must(err error) { + if err != nil { + println(err.Error()) + panic(err) } } From e53a3e93778b3c0d62b7162ccc107ba9a34d361e Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 30 Mar 2025 15:55:25 +0300 Subject: [PATCH 088/196] fix: minor rename --- examples/new-api/components/ids.go | 2 +- examples/new-api/systems/spatial-audio.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go index e6297596..4c3c374a 100644 --- a/examples/new-api/components/ids.go +++ b/examples/new-api/components/ids.go @@ -29,5 +29,5 @@ const ( WeaponComponentId SpaceshipIntentComponentId AsteroidSceneManagerComponentId - SoundEffectId + SoundEffectManagerComponentId ) diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go index 419a3024..a9e45037 100644 --- a/examples/new-api/systems/spatial-audio.go +++ b/examples/new-api/systems/spatial-audio.go @@ -75,7 +75,7 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { return true } - pan := s.CalculatePan(playerPos.XY, position.XY) + pan := s.calculatePan(playerPos.XY, position.XY) rl.SetSoundPan(*clip, pan) return true @@ -84,7 +84,7 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { func (s *SpatialAudioSystem) Destroy() { } -func (s *SpatialAudioSystem) CalculatePan(listener vectors.Vec2, source vectors.Vec2) float32 { +func (s *SpatialAudioSystem) calculatePan(listener vectors.Vec2, source vectors.Vec2) float32 { dx := float64(source.X - listener.X) dy := float64(source.Y - listener.Y) distanceSq := dx*dx + dy*dy From 7e2fa06e6d75a1ab7e6a62e9887aceb425d01b81 Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 30 Mar 2025 16:07:26 +0300 Subject: [PATCH 089/196] fix(sound-effects-manager): rename component id --- examples/new-api/components/audio.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/new-api/components/audio.go b/examples/new-api/components/audio.go index 8d417755..079a7a08 100644 --- a/examples/new-api/components/audio.go +++ b/examples/new-api/components/audio.go @@ -34,5 +34,5 @@ type SoundEffect struct { type SoundEffectsComponentManager = ecs.ComponentManager[SoundEffect] func NewSoundEffectsComponentManager() SoundEffectsComponentManager { - return ecs.NewComponentManager[SoundEffect](SoundEffectId) + return ecs.NewComponentManager[SoundEffect](SoundEffectManagerComponentId) } From 6a6ec9e4a095b7bead3a73df64c92be8e3aaa33d Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 30 Mar 2025 17:09:23 +0300 Subject: [PATCH 090/196] update sdl3 purego and cgo internals --- internal/raylib/main.go | 101 ++++++++++++++++++++++++++++++ internal/sdl3-cgo/sdl3cgo-game.go | 5 +- internal/sdl3-pure/sdl-game.go | 4 +- taskfile.yml | 9 ++- 4 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 internal/raylib/main.go diff --git a/internal/raylib/main.go b/internal/raylib/main.go new file mode 100644 index 00000000..6588cbed --- /dev/null +++ b/internal/raylib/main.go @@ -0,0 +1,101 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "fmt" + "log" + "os" + "runtime" + "runtime/pprof" + "time" + + "github.com/gen2brain/raylib-go/raylib" +) + +const ( + screenWidth = 1280 + screenHeight = 720 + batchSize = 1 << 14 + rectCounter = 1 << 18 + framerate = 600 + frameCount = 100 // Number of frames to average +) + +func main() { + f, err := os.Create("cpu.out") + if err != nil { + log.Fatal(err) + } + defer f.Close() + + err = pprof.StartCPUProfile(f) + if err != nil { + log.Fatal(err) + } + defer pprof.StopCPUProfile() + + fmt.Println("CPU Profile Started") + defer fmt.Println("CPU Profile Stopped") + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + rl.InitWindow(screenWidth, screenHeight, "raylib example") + defer rl.CloseWindow() + + rl.SetTargetFPS(framerate) + + rects := make([]rl.Rectangle, rectCounter) + for i := range rects { + rects[i] = rl.Rectangle{X: 300, Y: 300, Width: 10, Height: 10} + } + + var dt time.Duration = time.Second + var fps, avgFps float64 + updatedAt := time.Now() + + frameTimes := make([]float64, 0, frameCount) + + for !rl.WindowShouldClose() { + fps = float64(time.Second) / float64(dt) + dt = time.Since(updatedAt) + updatedAt = time.Now() + + frameTimes = append(frameTimes, fps) + if len(frameTimes) > frameCount { + frameTimes = frameTimes[1:] + } + + var totalFps float64 + for _, frameTime := range frameTimes { + totalFps += frameTime + } + avgFps = totalFps / float64(len(frameTimes)) + + rl.BeginDrawing() + rl.ClearBackground(rl.Black) + + rl.DrawText(fmt.Sprintf("FPS %f", fps), 10, 10, 20, rl.White) + rl.DrawText(fmt.Sprintf("Avg FPS %f", avgFps), 10, 30, 20, rl.White) + rl.DrawText(fmt.Sprintf("dt %s", dt.String()), 10, 50, 20, rl.White) + + for _, rect := range rects { + rl.DrawRectangleRec(rect, rl.White) + } + + rl.EndDrawing() + } +} diff --git a/internal/sdl3-cgo/sdl3cgo-game.go b/internal/sdl3-cgo/sdl3cgo-game.go index ccb1764b..e8bce7e9 100644 --- a/internal/sdl3-cgo/sdl3cgo-game.go +++ b/internal/sdl3-cgo/sdl3cgo-game.go @@ -35,7 +35,7 @@ func main() { runtime.LockOSThread() defer runtime.UnlockOSThread() - sdl.SetHint(sdl.HintRenderDriver, "gpu") + sdl.SetHint(sdl.HintRenderDriver, "gpu,software") must(sdl.Init(sdl.InitVideo)) defer sdl.Quit() @@ -44,6 +44,9 @@ func main() { defer w.Destroy() defer r.Destroy() + n, err := r.Name() + fmt.Println(n, sdl.GetRenderDriver(0)) + var renderTicker *time.Ticker if framerate > 0 { renderTicker = time.NewTicker(time.Second / time.Duration(framerate)) diff --git a/internal/sdl3-pure/sdl-game.go b/internal/sdl3-pure/sdl-game.go index 2cce6539..e89b1f46 100644 --- a/internal/sdl3-pure/sdl-game.go +++ b/internal/sdl3-pure/sdl-game.go @@ -34,7 +34,7 @@ func main() { runtime.LockOSThread() defer runtime.UnlockOSThread() - sdl.SetHint(sdl.HintRenderDriver, "gpu") + sdl.SetHint(sdl.HintRenderDriver, "gpu,software") defer sdl.Quit() if !sdl.Init(sdl.InitVideo) { panic(sdl.GetError()) @@ -49,6 +49,8 @@ func main() { defer sdl.DestroyRenderer(r) defer sdl.DestroyWindow(w) + fmt.Println(sdl.GetRendererName(r), sdl.GetRenderDriver(0)) + var dt time.Duration = time.Second var fps, avgFps float64 updatedAt := time.Now() diff --git a/taskfile.yml b/taskfile.yml index fd27744f..c66bba3a 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -46,13 +46,18 @@ tasks: cmds: - go tool trace trace.out + build-sdl: + env: + CGO_ENABLED: 1 + cmds: + - go build -o ./.dist/sdl3-cgo-game.exe internal/sdl3-cgo/sdl3cgo-game.go + - go build -o ./.dist/sdl3-purego-game.exe internal/sdl3-pure/sdl-game.go + build-win64: env: CGO_ENABLED: 1 cmds: - go build -o ./.dist/game-win64.exe -tags opengl43 examples/new-api/game.go - # - go build -o ./.dist/cgo-game.exe internal/sdl3-pure/sdl-game.go - # - CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -tags static -ldflags "-s -w" internal/sdl2/sdl-game.go build-mac: - task: build-darwin-amd64 From 2fd7689d22eeb00d474d8e904d02b58848fc4412 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 30 Mar 2025 18:14:25 +0300 Subject: [PATCH 091/196] feat sdl purego zukko internal test --- go.mod | 4 +- go.sum | 6 ++ internal/sdl3-pure-zukko/sdl-zukko.go | 139 ++++++++++++++++++++++++++ taskfile.yml | 1 + 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 internal/sdl3-pure-zukko/sdl-zukko.go diff --git a/go.mod b/go.mod index 0957d37e..56714241 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,8 @@ require ( ) require ( + github.com/Zyko0/go-sdl3 v0.0.0-20250324113244-771f317184f7 // indirect + github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/fgprof v0.9.5 // indirect github.com/francoispqt/gojay v1.2.13 // indirect @@ -51,7 +53,7 @@ require ( require ( github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect github.com/ebitengine/hideconsole v1.0.0 // indirect - github.com/ebitengine/purego v0.8.2 // indirect + github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b // indirect github.com/gorilla/sessions v1.4.0 github.com/gorilla/websocket v1.5.3 github.com/jezek/xgb v1.1.1 // indirect diff --git a/go.sum b/go.sum index 77b3b303..1d9febfb 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,10 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Zyko0/go-sdl3 v0.0.0-20250324113244-771f317184f7 h1:jlrgDnYn7sUJqiKp5n6t2t9IwJt427DN6zoallnRJMU= +github.com/Zyko0/go-sdl3 v0.0.0-20250324113244-771f317184f7/go.mod h1:y9Xormjz/GWcAeGbl+Z2zOaKtKfeeZ+5x8UiojyFFdg= +github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a h1:N5RnQ3/z1/HjCivGNfRbID/3QreOQ6Dulr3FAOgULqc= +github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a/go.mod h1:yJn8avSZmgophSwHMUDhlEIzdkB9uUTEctszmQmjxDo= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -35,6 +39,8 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b h1:/KAOJuXR4cWaQIiA9xBMDSQJ1JXq5gZHdSK8prrtUqQ= +github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= diff --git a/internal/sdl3-pure-zukko/sdl-zukko.go b/internal/sdl3-pure-zukko/sdl-zukko.go new file mode 100644 index 00000000..a86c26a7 --- /dev/null +++ b/internal/sdl3-pure-zukko/sdl-zukko.go @@ -0,0 +1,139 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "fmt" + "github.com/Zyko0/go-sdl3/bin/binsdl" + "github.com/Zyko0/go-sdl3/sdl" + "log" + "os" + "runtime" + "runtime/pprof" + "time" +) + +const batchSize = 1 << 14 +const rectCounter = 1 << 18 +const framerate = 600 +const frameCount = 100 // Number of frames to average + +func main() { + f, err := os.Create("cpu.out") + if err != nil { + log.Fatal(err) + } + defer f.Close() + + err = pprof.StartCPUProfile(f) + if err != nil { + log.Fatal(err) + } + defer pprof.StopCPUProfile() + + fmt.Println("CPU Profile Started") + defer fmt.Println("CPU Profile Stopped") + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + defer binsdl.Load().Unload() // sdl.LoadLibrary(sdl.Path()) + + sdl.SetHint(sdl.HINT_RENDER_DRIVER, "gpu,software") // <- gpu driver creates memory leak + if err := sdl.Init(sdl.INIT_VIDEO); err != nil { + panic(err.Error()) + } + defer sdl.Quit() + + //var w *sdl.Window + //var r *sdl.Renderer + w, r, err := sdl.CreateWindowAndRenderer("Hello, World!", 1280, 720, sdl.WINDOW_RESIZABLE) + if err != nil { + panic(err.Error()) + } + defer r.Destroy() + defer w.Destroy() + + var dt time.Duration = time.Second + var fps, avgFps float64 + updatedAt := time.Now() + + var renderTicker *time.Ticker + if framerate > 0 { + renderTicker = time.NewTicker(time.Second / time.Duration(framerate)) + defer renderTicker.Stop() + } + + rects := make([]sdl.FRect, rectCounter) + for i := range rects { + rects[i] = sdl.FRect{X: 300, Y: 300, W: 10, H: 10} + } + + fmt.Println(sdl.GetRenderDriver(0)) + frameTimes := make([]float64, 0, frameCount) + +Outer: + for { + if renderTicker != nil { + <-renderTicker.C + } + fps = time.Second.Seconds() / dt.Seconds() + dt = time.Since(updatedAt) + updatedAt = time.Now() + + frameTimes = append(frameTimes, fps) + if len(frameTimes) > frameCount { + frameTimes = frameTimes[1:] + } + + var totalFps float64 + for _, frameTime := range frameTimes { + totalFps += frameTime + } + avgFps = totalFps / float64(len(frameTimes)) + + var event sdl.Event + for sdl.PollEvent(&event) { + switch event.Type { + case sdl.EVENT_QUIT: + break Outer + case sdl.EVENT_KEY_DOWN: + if event.KeyboardEvent().Scancode == sdl.SCANCODE_ESCAPE { + break Outer + } + } + } + r.SetDrawColor(0, 0, 0, 255) + r.Clear() + + r.SetDrawColor(255, 255, 255, 255) + for i := 0; i < len(rects); i += batchSize { + r.RenderFillRects(rects[i : i+batchSize]) + } + + r.DebugText(10, 10, fmt.Sprintf("FPS %f", fps)) + r.DebugText(10, 20, fmt.Sprintf("Avg FPS %f", avgFps)) + r.DebugText(10, 30, fmt.Sprintf("dt %s", dt.String())) + + r.Present() + } +} + +func must(err error) { + if err != nil { + println(err.Error()) + panic(err) + } +} diff --git a/taskfile.yml b/taskfile.yml index c66bba3a..34559675 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -52,6 +52,7 @@ tasks: cmds: - go build -o ./.dist/sdl3-cgo-game.exe internal/sdl3-cgo/sdl3cgo-game.go - go build -o ./.dist/sdl3-purego-game.exe internal/sdl3-pure/sdl-game.go + - go build -o ./.dist/sdl3-purego-zukko-game.exe internal/sdl3-pure-zukko/sdl-zukko.go build-win64: env: From 9835d08eedc7c02959fc8987f480e0973ad01300 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 30 Mar 2025 18:35:32 +0300 Subject: [PATCH 092/196] feat ebiten internal test --- internal/ebiten/main.go | 104 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 internal/ebiten/main.go diff --git a/internal/ebiten/main.go b/internal/ebiten/main.go new file mode 100644 index 00000000..54e2a09d --- /dev/null +++ b/internal/ebiten/main.go @@ -0,0 +1,104 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "fmt" + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" + "github.com/hajimehoshi/ebiten/v2/vector" + "image/color" + "log" + "time" +) + +const ( + screenWidth = 1280 + screenHeight = 720 + rectCounter = 1 << 18 + frameCount = 100 +) + +type Rect struct { + X float32 + Y float32 + W float32 + H float32 +} + +type Game struct { + rects []Rect + frameTimes []float64 + updatedAt time.Time + dt time.Duration + fps float64 + avgFps float64 +} + +func (g *Game) Update() error { + g.dt = time.Since(g.updatedAt) + g.updatedAt = time.Now() + g.fps = 1.0 / g.dt.Seconds() + + g.frameTimes = append(g.frameTimes, g.fps) + if len(g.frameTimes) > frameCount { + g.frameTimes = g.frameTimes[1:] + } + + var totalFps float64 + for _, frameTime := range g.frameTimes { + totalFps += frameTime + } + g.avgFps = totalFps / float64(len(g.frameTimes)) + + if ebiten.IsKeyPressed(ebiten.KeyEscape) { + return fmt.Errorf("quit") + } + + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { + screen.Fill(color.Black) + + for _, rect := range g.rects { + vector.DrawFilledRect(screen, rect.X, rect.Y, rect.W, rect.H, color.White, false) + } + + ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f\nAvg FPS: %0.2f\nDT: %s", g.fps, g.avgFps, g.dt)) +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { + return screenWidth, screenHeight +} + +func main() { + game := &Game{ + rects: make([]Rect, rectCounter), + frameTimes: make([]float64, 0, frameCount), + updatedAt: time.Now(), + } + + for i := range game.rects { + game.rects[i] = Rect{X: 300, Y: 300, W: 10, H: 10} + } + + ebiten.SetWindowSize(screenWidth, screenHeight) + ebiten.SetWindowTitle("Hello, World!") + + if err := ebiten.RunGame(game); err != nil && err.Error() != "quit" { + log.Fatal(err) + } +} From f63de3fbf3b41c2c64526bbc01c132b8a58ed4d1 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 31 Mar 2025 03:53:54 +0300 Subject: [PATCH 093/196] feat sdl2 internal test --- Roboto-SemiBold.ttf | Bin 0 -> 146760 bytes internal/sdl2/sdl-game.go | 138 ++++++++++++++++++++++++++------------ 2 files changed, 96 insertions(+), 42 deletions(-) create mode 100644 Roboto-SemiBold.ttf diff --git a/Roboto-SemiBold.ttf b/Roboto-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3f348341cb5b76e13dbed0555a58e964d71f8613 GIT binary patch literal 146760 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%0Dqg^?pU4hZA$mrFU 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 literal 0 HcmV?d00001 diff --git a/internal/sdl2/sdl-game.go b/internal/sdl2/sdl-game.go index 2a37e1ff..18e5f738 100644 --- a/internal/sdl2/sdl-game.go +++ b/internal/sdl2/sdl-game.go @@ -2,60 +2,102 @@ package main import ( "fmt" + "github.com/veandco/go-sdl2/sdl" + "github.com/veandco/go-sdl2/ttf" "log" "os" + "runtime" + "runtime/pprof" "time" - - "github.com/veandco/go-sdl2/sdl" -) - -const ( - fontPath = "test.ttf" - fontSize = 32 ) -var winTitle string = "Go-SDL2 RenderAssterodd" -var winWidth, winHeight int32 = 800, 600 +const batchSize = 1 << 14 +const rectCounter = 1 << 18 +const framerate = 600 +const frameCount = 100 // Number of frames to average -func run() int { - var window *sdl.Window - var renderer *sdl.Renderer - - var rects []sdl.FRect = make([]sdl.FRect, 100_000) - - window, err := sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, - winWidth, winHeight, sdl.WINDOW_SHOWN) +func main() { + f, err := os.Create("cpu.out") if err != nil { - fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err) - return 1 + log.Fatal(err) } - defer window.Destroy() + defer f.Close() - renderer, err = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED) + err = pprof.StartCPUProfile(f) if err != nil { - fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", err) - return 2 + log.Fatal(err) } + defer pprof.StopCPUProfile() + + fmt.Println("CPU Profile Started") + defer fmt.Println("CPU Profile Stopped") + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ok := sdl.SetHint(sdl.HINT_RENDER_DRIVER, "gpu,software") + if !ok { + panic(sdl.GetError()) + } + must(ttf.Init()) + defer ttf.Quit() + must(sdl.Init(sdl.INIT_EVERYTHING)) + defer sdl.Quit() + + window, renderer, err := sdl.CreateWindowAndRenderer(1280, 720, sdl.WINDOW_RESIZABLE) + must(err) + defer window.Destroy() defer renderer.Destroy() - for i := range len(rects) { - rects[i] = sdl.FRect{X: float32(i%780) + 10, Y: float32(i / 780), W: 1, H: 1} + font, err := ttf.OpenFont("Roboto-SemiBold.ttf", 14) + must(err) + defer font.Close() + + var renderTicker *time.Ticker + if framerate > 0 { + renderTicker = time.NewTicker(time.Second / time.Duration(framerate)) + defer renderTicker.Stop() } var dt time.Duration = time.Second - var fps int64 + var fps, avgFps float64 updatedAt := time.Now() - running := true - for running { - fps = int64(1 / dt.Seconds()) + rects := make([]sdl.FRect, rectCounter) + for i := range rects { + rects[i] = sdl.FRect{X: 300, Y: 300, W: 10, H: 10} + } + + frameTimes := make([]float64, 0, frameCount) + +Outer: + for { + if renderTicker != nil { + <-renderTicker.C + } + fps = time.Second.Seconds() / dt.Seconds() dt = time.Since(updatedAt) updatedAt = time.Now() + frameTimes = append(frameTimes, fps) + if len(frameTimes) > frameCount { + frameTimes = frameTimes[1:] + } + + var totalFps float64 + for _, frameTime := range frameTimes { + totalFps += frameTime + } + avgFps = totalFps / float64(len(frameTimes)) + for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { switch event.(type) { case *sdl.QuitEvent: - running = false + break Outer + case *sdl.KeyboardEvent: + if event.(*sdl.KeyboardEvent).Keysym.Scancode == sdl.SCANCODE_ESCAPE { + break Outer + } } } @@ -63,29 +105,41 @@ func run() int { must(renderer.Clear()) must(renderer.SetDrawColor(255, 255, 255, 255)) - //rect = sdl.FRect{300, 0, 200, 200} - //for range 100_000 { - // must(renderer.DrawRectF(&rect)) - //} + for i := 0; i < len(rects); i += batchSize { + must(renderer.FillRectsF(rects[i : i+batchSize])) + } - //must(text.(nil, renderer, &sdl.Rect{X: 400 - (text.W / 2), Y: 300 - (text.H / 2), W: 0, H: 0})) + must(drawText(renderer, font, 10, 10, fmt.Sprintf("FPS %f", fps))) + must(drawText(renderer, font, 10, 30, fmt.Sprintf("Avg FPS %f", avgFps))) + must(drawText(renderer, font, 10, 50, fmt.Sprintf("dt %s", dt.String()))) - must(renderer.FillRectsF(rects)) renderer.Present() - log.Printf("FPS: %d\n", fps) } - - return 0 } func must(err error) { if err != nil { - log.Println(err) + println(err.Error()) panic(err) } } -func main() { - os.Exit(run()) +// this is bad approach to draw a text because of creating a texture on each text change on each frame +// it should be a texture atlas with a rendered chars, and then you copy part of it to the destination char by char +func drawText(renderer *sdl.Renderer, font *ttf.Font, x, y int, text string) error { + surface, err := font.RenderUTF8Blended(text, sdl.Color{R: 255, G: 255, B: 255, A: 255}) + if err != nil { + return err + } + defer surface.Free() + + texture, err := renderer.CreateTextureFromSurface(surface) + if err != nil { + return err + } + defer texture.Destroy() + + dst := sdl.Rect{X: int32(x), Y: int32(y), W: surface.W, H: surface.H} + return renderer.Copy(texture, nil, &dst) } From 4dd954d1a8b9f9a059ac3cce8a122422784f2559 Mon Sep 17 00:00:00 2001 From: bitver Date: Mon, 31 Mar 2025 13:34:18 +0500 Subject: [PATCH 094/196] +perf --- internal/sdl2/sdl-game.go | 68 +++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/internal/sdl2/sdl-game.go b/internal/sdl2/sdl-game.go index 18e5f738..a9dd98df 100644 --- a/internal/sdl2/sdl-game.go +++ b/internal/sdl2/sdl-game.go @@ -125,21 +125,61 @@ func must(err error) { } } -// this is bad approach to draw a text because of creating a texture on each text change on each frame -// it should be a texture atlas with a rendered chars, and then you copy part of it to the destination char by char -func drawText(renderer *sdl.Renderer, font *ttf.Font, x, y int, text string) error { - surface, err := font.RenderUTF8Blended(text, sdl.Color{R: 255, G: 255, B: 255, A: 255}) - if err != nil { - return err - } - defer surface.Free() +// Cache all char textures on the fly +// TODO: preload all chars initially (maybe +1FPS) +var letterCache [256]*sdl.Texture + +func drawText(renderer *sdl.Renderer, font *ttf.Font, x, y int32, text string) error { + var totalWidth int32 + var maxHeight int32 + + for _, char := range text { + if char < 256 { + if texture := letterCache[char]; texture != nil { + _, _, w, h, err := texture.Query() + if err != nil { + return err + } + dst := sdl.Rect{X: x + totalWidth, Y: y, W: w, H: h} + if err := renderer.Copy(texture, nil, &dst); err != nil { + return err + } + totalWidth += w + if h > maxHeight { + maxHeight = h + } + continue + } + } - texture, err := renderer.CreateTextureFromSurface(surface) - if err != nil { - return err + surface, err := font.RenderUTF8Blended(string(char), sdl.Color{R: 255, G: 255, B: 255, A: 255}) + if err != nil { + return err + } + defer surface.Free() + + texture, err := renderer.CreateTextureFromSurface(surface) + if err != nil { + return err + } + if char < 256 { + letterCache[char] = texture + } + + _, _, w, h, err := texture.Query() + if err != nil { + return err + } + + dst := sdl.Rect{X: x + totalWidth, Y: y, W: w, H: h} + if err := renderer.Copy(texture, nil, &dst); err != nil { + return err + } + totalWidth += w + if h > maxHeight { + maxHeight = h + } } - defer texture.Destroy() - dst := sdl.Rect{X: int32(x), Y: int32(y), W: surface.W, H: surface.H} - return renderer.Copy(texture, nil, &dst) + return nil } From 76d17e67def6450debd2832d41243877bfb6ff3a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 31 Mar 2025 22:01:28 +0300 Subject: [PATCH 095/196] upd bvh tree --- pkg/bvh/components.go | 11 ----------- pkg/bvh/tree.go | 28 ++++++++++++---------------- 2 files changed, 12 insertions(+), 27 deletions(-) delete mode 100644 pkg/bvh/components.go diff --git a/pkg/bvh/components.go b/pkg/bvh/components.go deleted file mode 100644 index 527f83e3..00000000 --- a/pkg/bvh/components.go +++ /dev/null @@ -1,11 +0,0 @@ -package bvh - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -type treeComponent struct { - Entity ecs.Entity - AABB *stdcomponents.AABB -} diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index e5b3a7c2..535ad7db 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -78,12 +78,10 @@ func (t *Tree) Build() { t.codes.Reset() // Extract and sort components by morton code - var componentsSlice = make([]component, 0, t.components.Len()) - for i := 0; i < t.components.Len(); i++ { - componentsSlice = append(componentsSlice, t.components.GetValue(i)) - } + var componentsSlice = t.components.Raw(make([]component, 0, t.components.Len())) + slices.SortFunc(componentsSlice, func(a, b component) int { - return int(a.code) - int(b.code) + return int(a.code - b.code) }) // Add leaves @@ -187,14 +185,12 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity // Use stack-based traversal const stackSize = 32 - stack := [stackSize]int{} - stackPtr := 0 - stack[stackPtr] = 0 - stackPtr++ - - for stackPtr > 0 { - stackPtr-- - nodeIndex := stack[stackPtr] + stack := [stackSize]int{0} + stackLen := 1 + + for stackLen > 0 { + stackLen-- + nodeIndex := stack[stackLen] a := t.aabbNodes.Get(nodeIndex) b := aabb @@ -212,9 +208,9 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity } // Push child indices (right and left) onto the stack. - stack[stackPtr] = int(node.childIndex + 1) - stack[stackPtr+1] = int(node.childIndex) - stackPtr += 2 + stack[stackLen] = int(node.childIndex + 1) + stack[stackLen+1] = int(node.childIndex) + stackLen += 2 } return result From f30972247e287ea2a457af71bab89eabcd6398fd Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 31 Mar 2025 23:50:12 +0300 Subject: [PATCH 096/196] upd collision detection massive reallocations --- examples/new-api/systems/asterodd.go | 38 +++-- pkg/bvh/tree.go | 29 ++-- pkg/ecs/paged-array.go | 37 ++--- stdsystems/collision-detection-bvh.go | 196 ++++++++++++++------------ 4 files changed, 170 insertions(+), 130 deletions(-) diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index 725492dd..45fbf778 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -20,6 +20,8 @@ import ( "gomp/examples/new-api/entities" "gomp/pkg/ecs" "gomp/stdcomponents" + "gomp/vectors" + "math/rand" "time" ) @@ -28,14 +30,15 @@ func NewAssteroddSystem() AssteroddSystem { } type AssteroddSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - Velocities *stdcomponents.VelocityComponentManager - Sprites *stdcomponents.SpriteComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - RigidBodies *stdcomponents.RigidBodyComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Velocities *stdcomponents.VelocityComponentManager + Sprites *stdcomponents.SpriteComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager PlayerTags *components.PlayerTagComponentManager AsteroidTags *components.AsteroidComponentManager @@ -98,6 +101,25 @@ func (s *AssteroddSystem) Init() { entities.CreateWall(&wallManager, -1000, -1000, 0, 1000, 7000) entities.CreateWall(&wallManager, 5000, -1000, 0, 1000, 7000) + for range 30000 { + randPos := vectors.Vec2{ + X: float32(rand.Intn(5000)), + Y: float32(rand.Intn(5000)), + } + entities.CreateBullet(entities.CreateBulletManagers{ + EntityManager: s.EntityManager, + Positions: s.Positions, + Rotations: s.Rotations, + Scales: s.Scales, + Velocities: s.Velocities, + CircleColliders: s.CircleColliders, + RigidBodies: s.RigidBodies, + Sprites: s.Sprites, + BulletTags: s.BulletTags, + Hps: s.Hps, + }, randPos.X, randPos.Y, 0, 0, 0) + } + manager := s.EntityManager.Create() s.SceneManager.Create(manager, components.AsteroidSceneManager{}) } diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 535ad7db..5ec874c1 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -57,6 +57,8 @@ type Tree struct { codes ecs.PagedArray[uint32] components ecs.PagedArray[component] layer stdcomponents.CollisionLayer + + componentsSlice []component } func (t *Tree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { @@ -78,15 +80,19 @@ func (t *Tree) Build() { t.codes.Reset() // Extract and sort components by morton code - var componentsSlice = t.components.Raw(make([]component, 0, t.components.Len())) + if cap(t.componentsSlice) < t.components.Len() { + t.componentsSlice = make([]component, 0, t.components.Len()) + } - slices.SortFunc(componentsSlice, func(a, b component) int { + t.componentsSlice = t.components.Raw(t.componentsSlice) + + slices.SortFunc(t.componentsSlice, func(a, b component) int { return int(a.code - b.code) }) // Add leaves - for i := range componentsSlice { - component := &componentsSlice[i] + for i := range t.componentsSlice { + component := &t.componentsSlice[i] t.leaves.Append(leaf{id: component.entity}) t.aabbLeaves.Append(component.aabb) t.codes.Append(component.code) @@ -129,10 +135,8 @@ func (t *Tree) Build() { // Create left and right nodes leftIndex := t.nodes.Len() - t.nodes.Append(node{-1}) - t.nodes.Append(node{-1}) - t.aabbNodes.Append(stdcomponents.AABB{}) - t.aabbNodes.Append(stdcomponents.AABB{}) + t.nodes.Append(node{-1}, node{-1}) + t.aabbNodes.Append(stdcomponents.AABB{}, stdcomponents.AABB{}) // Set parent's childIndex to leftIndex t.nodes.Get(task.parentIndex).childIndex = int32(leftIndex) @@ -172,6 +176,7 @@ func (t *Tree) Build() { t.aabbNodes.Set(task.parentIndex, merged) } } + t.components.Reset() } func (t *Tree) Layer() stdcomponents.CollisionLayer { @@ -185,12 +190,12 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity // Use stack-based traversal const stackSize = 32 - stack := [stackSize]int{0} + stack := [stackSize]int32{0} stackLen := 1 for stackLen > 0 { stackLen-- - nodeIndex := stack[stackLen] + nodeIndex := int(stack[stackLen]) a := t.aabbNodes.Get(nodeIndex) b := aabb @@ -208,8 +213,8 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity } // Push child indices (right and left) onto the stack. - stack[stackLen] = int(node.childIndex + 1) - stack[stackLen+1] = int(node.childIndex) + stack[stackLen] = node.childIndex + 1 + stack[stackLen+1] = node.childIndex stackLen += 2 } diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 05891728..c82de76a 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -22,7 +22,7 @@ type PagedArray[T any] struct { func NewPagedArray[T any]() (a PagedArray[T]) { a.book = make([]ArrayPage[T], 2, initialBookSize) - a.parallelCount = uint8(runtime.NumCPU()) / 2 + a.parallelCount = uint8(runtime.NumCPU()) - 2 return a } @@ -63,26 +63,31 @@ func (a *PagedArray[T]) Set(index int, value T) *T { return &page.data[index] } -func (a *PagedArray[T]) Append(value T) *T { - if a.currentPageIndex >= len(a.book) { - newBooks := make([]ArrayPage[T], len(a.book)*2) - a.book = append(a.book, newBooks...) - } - - page := &a.book[a.currentPageIndex] - - if page.len == pageSize { - a.currentPageIndex++ +func (a *PagedArray[T]) Append(values ...T) *T { + var result *T + for i := range values { + value := values[i] if a.currentPageIndex >= len(a.book) { newBooks := make([]ArrayPage[T], len(a.book)*2) a.book = append(a.book, newBooks...) } - page = &a.book[a.currentPageIndex] + + page := &a.book[a.currentPageIndex] + + if page.len == pageSize { + a.currentPageIndex++ + if a.currentPageIndex >= len(a.book) { + newBooks := make([]ArrayPage[T], len(a.book)*2) + a.book = append(a.book, newBooks...) + } + page = &a.book[a.currentPageIndex] + } + page.data[page.len] = value + result = &page.data[page.len] + page.len++ + a.len++ } - page.data[page.len] = value - result := &page.data[page.len] - page.len++ - a.len++ + return result } diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 3c1eeff7..5e2b9693 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -31,6 +31,8 @@ func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { return CollisionDetectionBVHSystem{ activeCollisions: make(map[CollisionPair]ecs.Entity), collisionEvents: make([]ecs.PagedArray[CollisionEvent], maxNumWorkers), + trees: make([]bvh.Tree, 0, 8), + treesLookup: make(map[stdcomponents.CollisionLayer]int, 8), } } @@ -55,6 +57,7 @@ type CollisionDetectionBVHSystem struct { activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities currentCollisions map[CollisionPair]struct{} + entities []ecs.Entity } func (s *CollisionDetectionBVHSystem) Init() { @@ -71,10 +74,6 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { return } - // Init trees - s.trees = make([]bvh.Tree, 0, 8) - s.treesLookup = make(map[stdcomponents.CollisionLayer]int, 8) - // Fill trees s.AABB.EachEntity(func(entity ecs.Entity) bool { aabb := s.AABB.Get(entity) @@ -103,39 +102,19 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } wg.Wait() - entities := s.AABB.RawEntities(make([]ecs.Entity, 0, s.AABB.Len())) - - s.findEntityCollisions(entities) -} - -func (s *CollisionDetectionBVHSystem) Destroy() {} - -func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity) { - var wg sync.WaitGroup - entitiesLength := len(entities) - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(entitiesLength/128, maxNumWorkers), 1) - chunkSize := entitiesLength / numWorkers - - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = entitiesLength - } - - go func(start int, end int, id int) { - defer wg.Done() - - for i := range entities[start:end] { - entity := entities[i+startIndex] - s.broadPhase(entity, id) - } - }(startIndex, endIndex, workedId) + if len(s.entities) < s.AABB.Len() { + s.entities = make([]ecs.Entity, 0, s.AABB.Len()) } - // Wait for workers and close collision channel - wg.Wait() + s.entities = s.AABB.RawEntities(s.entities) + s.findEntityCollisions(s.entities) + + // could be used, but needs a worker id info + //s.AABB.EachEntityParallel(func(entity ecs.Entity) bool { + // + // potentialEntities := s.broadPhase(entity, make([]ecs.Entity, 0, 64)) + // s.narrowPhase(entity, potentialEntities, id) + // return true + //}) for i := range s.collisionEvents { events := &s.collisionEvents[i] @@ -176,19 +155,49 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity } } -func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, workerId int) { +func (s *CollisionDetectionBVHSystem) Destroy() {} + +func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity) { + var wg sync.WaitGroup + entitiesLength := len(entities) + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(entitiesLength/128, maxNumWorkers), 1) + chunkSize := entitiesLength / numWorkers + + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = entitiesLength + } + + go func(start int, end int, id int) { + defer wg.Done() + + for i := range entities[start:end] { + entityA := entities[i+startIndex] + + potentialEntities := s.broadPhase(entityA, make([]ecs.Entity, 0, 64)) + s.narrowPhase(entityA, potentialEntities, id) + } + }(startIndex, endIndex, workedId) + } + // Wait for workers and close collision channel + wg.Wait() +} + +func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, result []ecs.Entity) []ecs.Entity { colliderA := s.GenericCollider.Get(entityA) if colliderA.AllowSleep { isSleeping := s.ColliderSleepStateComponentManager.Get(entityA) if isSleeping != nil { - return + return result } } aabb := s.AABB.Get(entityA) - result := make([]ecs.Entity, 0, 64) - // Iterate through all trees for treeIndex := range s.trees { tree := &s.trees[treeIndex] @@ -202,72 +211,71 @@ func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, workerId in // Traverse this BVH tree for potential collisions result = tree.Query(aabb, result) } + return result +} - for _, entityB := range result { +func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialEntities []ecs.Entity, workerId int) { + for _, entityB := range potentialEntities { if entityA == entityB { continue } + colliderA := s.GenericCollider.Get(entityA) colliderB := s.GenericCollider.Get(entityB) - if collision, ok := s.narrowPhase(colliderA, colliderB, entityA, entityB); ok { - s.collisionEvents[workerId].Append(collision) + posA := s.Positions.Get(entityA) + posB := s.Positions.Get(entityB) + scaleA := s.Scales.Get(entityA) + scaleB := s.Scales.Get(entityB) + rotA := s.Rotations.Get(entityA) + rotB := s.Rotations.Get(entityB) + transformA := stdcomponents.Transform2d{ + Position: posA.XY, + Rotation: rotA.Angle, + Scale: scaleA.XY, + } + transformB := stdcomponents.Transform2d{ + Position: posB.XY, + Rotation: rotB.Angle, + Scale: scaleB.XY, } - } -} -func (s *CollisionDetectionBVHSystem) narrowPhase(colliderA, colliderB *stdcomponents.GenericCollider, entityA, entityB ecs.Entity) (e CollisionEvent, ok bool) { - posA := s.Positions.Get(entityA) - posB := s.Positions.Get(entityB) - scaleA := s.Scales.Get(entityA) - scaleB := s.Scales.Get(entityB) - rotA := s.Rotations.Get(entityA) - rotB := s.Rotations.Get(entityB) - transformA := stdcomponents.Transform2d{ - Position: posA.XY, - Rotation: rotA.Angle, - Scale: scaleA.XY, - } - transformB := stdcomponents.Transform2d{ - Position: posB.XY, - Rotation: rotB.Angle, - Scale: scaleB.XY, - } + circleA := s.CircleColliders.Get(entityA) + circleB := s.CircleColliders.Get(entityB) + if circleA != nil && circleB != nil { + radiusA := circleA.Radius * scaleA.XY.X + radiusB := circleB.Radius * scaleB.XY.X + if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { + s.collisionEvents[workerId].Append(CollisionEvent{ + entityA: entityA, + entityB: entityB, + position: transformA.Position, + normal: transformB.Position.Sub(transformA.Position).Normalize(), + depth: radiusA + radiusB - transformB.Position.Distance(transformA.Position), + }) + continue + } + } - circleA := s.CircleColliders.Get(entityA) - circleB := s.CircleColliders.Get(entityB) - if circleA != nil && circleB != nil { - radiusA := circleA.Radius * scaleA.XY.X - radiusB := circleB.Radius * scaleB.XY.X - if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { - return CollisionEvent{ - entityA: entityA, - entityB: entityB, - position: transformA.Position, - normal: transformB.Position.Sub(transformA.Position).Normalize(), - depth: radiusA + radiusB - transformB.Position.Distance(transformA.Position), - }, true + // GJK strategy + colA := s.getGjkCollider(colliderA, entityA) + colB := s.getGjkCollider(colliderB, entityB) + // First detect collision using GJK + simplex, collision := gjk.CheckCollision(colA, colB, &transformA, &transformB) + if !collision { + continue } - } - // GJK strategy - colA := s.getGjkCollider(colliderA, entityA) - colB := s.getGjkCollider(colliderB, entityB) - // First detect collision using GJK - simplex, collision := gjk.CheckCollision(colA, colB, &transformA, &transformB) - if !collision { - return e, false + // If collision detected, get penetration details using EPA + normal, depth := gjk.EPA(colA, colB, &transformA, &transformB, &simplex) + position := posA.XY.Add(posB.XY.Sub(posA.XY)) + s.collisionEvents[workerId].Append(CollisionEvent{ + entityA: entityA, + entityB: entityB, + position: position, + normal: normal, + depth: depth, + }) } - - // If collision detected, get penetration details using EPA - normal, depth := gjk.EPA(colA, colB, &transformA, &transformB, &simplex) - position := posA.XY.Add(posB.XY.Sub(posA.XY)) - return CollisionEvent{ - entityA: entityA, - entityB: entityB, - position: position, - normal: normal, - depth: depth, - }, true } func (s *CollisionDetectionBVHSystem) processExitStates() { From b9205b1cc96cde59a313b7d6553bb75f491813c4 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 1 Apr 2025 19:32:57 +0300 Subject: [PATCH 097/196] pre bvh tree fix --- examples/new-api/instances/component-list.go | 2 ++ examples/new-api/systems/asterodd.go | 2 +- examples/new-api/systems/render-assterodd.go | 12 ++++++-- stdcomponents/aabb.go | 4 +++ stdcomponents/ids.go | 2 ++ stdcomponents/tree-bvh.go | 30 +++++++++++++++++++ stdsystems/collision-detection-bvh.go | 31 ++++++++++++++++++-- 7 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 stdcomponents/tree-bvh.go diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index 052e75f4..8be65713 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -43,6 +43,7 @@ type ComponentList struct { AABB stdcomponents.AABBComponentManager SpatialIndex stdcomponents.SpatialIndexComponentManager RigidBody stdcomponents.RigidBodyComponentManager + BvhTree stdcomponents.BvhTreeComponentManager Health components.HpComponentManager Controller components.ControllerComponentManager @@ -82,6 +83,7 @@ func NewComponentList() ComponentList { AABB: stdcomponents.NewAABBComponentManager(), SpatialIndex: stdcomponents.NewSpatialIndexComponentManager(), RigidBody: stdcomponents.NewRigidBodyComponentManager(), + BvhTree: stdcomponents.NewBvhTreeComponentManager(), Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index 45fbf778..9d758ea6 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -101,7 +101,7 @@ func (s *AssteroddSystem) Init() { entities.CreateWall(&wallManager, -1000, -1000, 0, 1000, 7000) entities.CreateWall(&wallManager, 5000, -1000, 0, 1000, 7000) - for range 30000 { + for range 0 { randPos := vectors.Vec2{ X: float32(rand.Intn(5000)), Y: float32(rand.Intn(5000)), diff --git a/examples/new-api/systems/render-assterodd.go b/examples/new-api/systems/render-assterodd.go index b75d90ce..2f8fc5e8 100644 --- a/examples/new-api/systems/render-assterodd.go +++ b/examples/new-api/systems/render-assterodd.go @@ -51,6 +51,7 @@ type RenderAssteroddSystem struct { AABBs *stdcomponents.AABBComponentManager Collisions *stdcomponents.CollisionComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + BvhTrees *stdcomponents.BvhTreeComponentManager renderList []renderEntry instanceData []stdcomponents.RLTexturePro camera rl.Camera2D @@ -212,12 +213,17 @@ func (s *RenderAssteroddSystem) render() { rl.BeginMode2D(s.camera) s.AABBs.EachEntity(func(e ecs.Entity) bool { aabb := s.AABBs.Get(e) - color := rl.Green + clr := rl.Green isSleeping := s.ColliderSleepStateComponentManager.Get(e) if isSleeping != nil { - color = rl.Blue + clr = rl.Blue + } + isTree := s.BvhTrees.Get(e) + if isTree != nil { + rl.DrawRectangle(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), isTree.Color) + return true } - rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), color) + rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), clr) return true }) s.Collisions.EachEntity(func(entity ecs.Entity) bool { diff --git a/stdcomponents/aabb.go b/stdcomponents/aabb.go index 7bb7861b..c2ddfd9e 100644 --- a/stdcomponents/aabb.go +++ b/stdcomponents/aabb.go @@ -24,6 +24,10 @@ type AABB struct { Max vectors.Vec2 } +func (a *AABB) Center() vectors.Vec2 { + return a.Min.Add(a.Max).Scale(0.5) +} + type AABBComponentManager = ecs.ComponentManager[AABB] func NewAABBComponentManager() AABBComponentManager { diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 18f1be4c..71463f75 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -10,6 +10,7 @@ import ( "gomp/pkg/ecs" ) +// StdComponentIds MUST always be the last const ( InvalidComponentId ecs.ComponentId = iota TransformComponentId @@ -38,5 +39,6 @@ const ( SpatialIndexComponentId AABBComponentId RigidBodyComponentId + BvhTreeComponentId StdComponentIds ) diff --git a/stdcomponents/tree-bvh.go b/stdcomponents/tree-bvh.go new file mode 100644 index 00000000..e15e122f --- /dev/null +++ b/stdcomponents/tree-bvh.go @@ -0,0 +1,30 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" + "image/color" +) + +type BvhTree struct { + Color color.RGBA +} + +type BvhTreeComponentManager = ecs.ComponentManager[BvhTree] + +func NewBvhTreeComponentManager() BvhTreeComponentManager { + return ecs.NewComponentManager[BvhTree](BvhTreeComponentId) +} diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 5e2b9693..cc50baf8 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -20,6 +20,7 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" + "image/color" "runtime" "sync" "time" @@ -49,6 +50,7 @@ type CollisionDetectionBVHSystem struct { SpatialIndex *stdcomponents.SpatialIndexComponentManager AABB *stdcomponents.AABBComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + BvhTreeComponentManager *stdcomponents.BvhTreeComponentManager trees []bvh.Tree treesLookup map[stdcomponents.CollisionLayer]int @@ -70,12 +72,12 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { s.currentCollisions = make(map[CollisionPair]struct{}) defer s.processExitStates() - if s.AABB.Len() == 0 { + if s.GenericCollider.Len() == 0 { return } // Fill trees - s.AABB.EachEntity(func(entity ecs.Entity) bool { + s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { aabb := s.AABB.Get(entity) layer := s.GenericCollider.Get(entity).Layer @@ -102,10 +104,33 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } wg.Wait() + s.BvhTreeComponentManager.EachEntity(func(entity ecs.Entity) bool { + s.EntityManager.Delete(entity) + return true + }) + + for i := range s.trees { + tree := s.trees[i] + treeColor := color.RGBA{ + R: uint8(i * 255 / len(s.trees)), + G: uint8((i + 1) * 255 / len(s.trees)), + B: uint8((i + 2) * 255 / len(s.trees)), + A: 10, + } + tree.AabbNodes.AllData(func(aabb *stdcomponents.AABB) bool { + e := s.EntityManager.Create() + s.BvhTreeComponentManager.Create(e, stdcomponents.BvhTree{ + Color: treeColor, + }) + s.AABB.Create(e, *aabb) + return true + }) + } + if len(s.entities) < s.AABB.Len() { s.entities = make([]ecs.Entity, 0, s.AABB.Len()) } - s.entities = s.AABB.RawEntities(s.entities) + s.entities = s.GenericCollider.RawEntities(s.entities) s.findEntityCollisions(s.entities) // could be used, but needs a worker id info From 3ed71543d29be523fd99ab4d3a65c350faaf6a8f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 1 Apr 2025 21:05:50 +0300 Subject: [PATCH 098/196] bvh tree fix --- pkg/bvh/tree.go | 80 +++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 5ec874c1..1a700f23 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -18,7 +18,6 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" - "math" "math/bits" "slices" ) @@ -34,16 +33,16 @@ type leaf struct { type component struct { entity ecs.Entity aabb *stdcomponents.AABB - code uint32 + code uint64 } func NewTree(layer stdcomponents.CollisionLayer) Tree { return Tree{ nodes: ecs.NewPagedArray[node](), - aabbNodes: ecs.NewPagedArray[stdcomponents.AABB](), + AabbNodes: ecs.NewPagedArray[stdcomponents.AABB](), leaves: ecs.NewPagedArray[leaf](), - aabbLeaves: ecs.NewPagedArray[*stdcomponents.AABB](), - codes: ecs.NewPagedArray[uint32](), + AabbLeaves: ecs.NewPagedArray[*stdcomponents.AABB](), + codes: ecs.NewPagedArray[uint64](), components: ecs.NewPagedArray[component](), layer: layer, } @@ -51,10 +50,10 @@ func NewTree(layer stdcomponents.CollisionLayer) Tree { type Tree struct { nodes ecs.PagedArray[node] - aabbNodes ecs.PagedArray[stdcomponents.AABB] + AabbNodes ecs.PagedArray[stdcomponents.AABB] leaves ecs.PagedArray[leaf] - aabbLeaves ecs.PagedArray[*stdcomponents.AABB] - codes ecs.PagedArray[uint32] + AabbLeaves ecs.PagedArray[*stdcomponents.AABB] + codes ecs.PagedArray[uint64] components ecs.PagedArray[component] layer stdcomponents.CollisionLayer @@ -62,8 +61,7 @@ type Tree struct { } func (t *Tree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { - center := aabb.Min.Add(aabb.Max).Scale(0.5) - code := t.morton2D(center.X, center.Y) + code := t.morton2D(aabb) t.components.Append(component{ entity: entity, aabb: aabb, @@ -74,9 +72,9 @@ func (t *Tree) AddComponent(entity ecs.Entity, aabb *stdcomponents.AABB) { func (t *Tree) Build() { // Reset tree t.nodes.Reset() - t.aabbNodes.Reset() + t.AabbNodes.Reset() t.leaves.Reset() - t.aabbLeaves.Reset() + t.AabbLeaves.Reset() t.codes.Reset() // Extract and sort components by morton code @@ -94,7 +92,7 @@ func (t *Tree) Build() { for i := range t.componentsSlice { component := &t.componentsSlice[i] t.leaves.Append(leaf{id: component.entity}) - t.aabbLeaves.Append(component.aabb) + t.AabbLeaves.Append(component.aabb) t.codes.Append(component.code) } t.components.Reset() @@ -105,7 +103,7 @@ func (t *Tree) Build() { // Add root node t.nodes.Append(node{-1}) - t.aabbNodes.Append(stdcomponents.AABB{}) + t.AabbNodes.Append(stdcomponents.AABB{}) type buildTask struct { parentIndex int @@ -127,7 +125,7 @@ func (t *Tree) Build() { if task.start == task.end { // Leaf node t.nodes.Get(task.parentIndex).childIndex = -int32(task.start) - t.aabbNodes.Set(task.parentIndex, *t.aabbLeaves.GetValue(task.start)) + t.AabbNodes.Set(task.parentIndex, *t.AabbLeaves.GetValue(task.start)) continue } @@ -136,7 +134,7 @@ func (t *Tree) Build() { // Create left and right nodes leftIndex := t.nodes.Len() t.nodes.Append(node{-1}, node{-1}) - t.aabbNodes.Append(stdcomponents.AABB{}, stdcomponents.AABB{}) + t.AabbNodes.Append(stdcomponents.AABB{}, stdcomponents.AABB{}) // Set parent's childIndex to leftIndex t.nodes.Get(task.parentIndex).childIndex = int32(leftIndex) @@ -169,11 +167,11 @@ func (t *Tree) Build() { leftChildIndex := int(t.nodes.Get(task.parentIndex).childIndex) rightChildIndex := leftChildIndex + 1 - leftAABB := t.aabbNodes.Get(leftChildIndex) - rightAABB := t.aabbNodes.Get(rightChildIndex) + leftAABB := t.AabbNodes.Get(leftChildIndex) + rightAABB := t.AabbNodes.Get(rightChildIndex) merged := t.mergeAABB(leftAABB, rightAABB) - t.aabbNodes.Set(task.parentIndex, merged) + t.AabbNodes.Set(task.parentIndex, merged) } } t.components.Reset() @@ -196,7 +194,7 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity for stackLen > 0 { stackLen-- nodeIndex := int(stack[stackLen]) - a := t.aabbNodes.Get(nodeIndex) + a := t.AabbNodes.Get(nodeIndex) b := aabb // Early exit if no AABB overlap @@ -239,7 +237,7 @@ func (t *Tree) findSplit(start, end int) int { // Calculate the number of highest bits that are the same // for all objects, using the count-leading-zeros intrinsic. - commonPrefix := bits.LeadingZeros32(first ^ last) + commonPrefix := bits.LeadingZeros64(first ^ last) // Use binary search to find where the next bit differs. // Specifically, we are looking for the highest object that @@ -253,7 +251,7 @@ func (t *Tree) findSplit(start, end int) int { if newSplit < end { splitCode := t.codes.GetValue(newSplit) - splitPrefix := bits.LeadingZeros32(first ^ splitCode) + splitPrefix := bits.LeadingZeros64(first ^ splitCode) if splitPrefix > commonPrefix { split = newSplit } @@ -283,18 +281,36 @@ func (t *Tree) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { // Expands a 16-bit integer into 32 bits by inserting 1 zero after each bit func (t *Tree) expandBits2D(v uint32) uint32 { - v = (v | (v << 16)) & 0x030000FF - v = (v | (v << 8)) & 0x0300F00F - v = (v | (v << 4)) & 0x030C30C3 - v = (v | (v << 2)) & 0x09249249 + v = (v | (v << 8)) & 0x00FF00FF + v = (v | (v << 4)) & 0x0F0F0F0F + v = (v | (v << 2)) & 0x33333333 + v = (v | (v << 1)) & 0x55555555 return v } -const mortonPrecision = 1 << 16 +const mortonPrecision = (1 << 16) - 1 -// 2D Morton code for centroids coordinates in [0,1] range -func (t *Tree) morton2D(x, y float32) uint32 { - xx := uint32(math.Min(math.Max(float64(x)*mortonPrecision, 0.0), mortonPrecision-1)) - yy := uint32(math.Min(math.Max(float64(y)*mortonPrecision, 0.0), mortonPrecision-1)) - return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) +func (t *Tree) morton2D(aabb *stdcomponents.AABB) uint64 { + center := aabb.Center() + // Scale coordinates to 16-bit integers + + xx := uint64(center.X * mortonPrecision) + yy := uint64(center.Y * mortonPrecision) + + // Spread the bits of x into the even positions + xx = (xx | (xx << 16)) & 0x0000FFFF0000FFFF + xx = (xx | (xx << 8)) & 0x00FF00FF00FF00FF + xx = (xx | (xx << 4)) & 0x0F0F0F0F0F0F0F0F + xx = (xx | (xx << 2)) & 0x3333333333333333 + xx = (xx | (xx << 1)) & 0x5555555555555555 + + // Spread the bits of y into the even positions and shift to odd positions + yy = (yy | (yy << 16)) & 0x0000FFFF0000FFFF + yy = (yy | (yy << 8)) & 0x00FF00FF00FF00FF + yy = (yy | (yy << 4)) & 0x0F0F0F0F0F0F0F0F + yy = (yy | (yy << 2)) & 0x3333333333333333 + yy = (yy | (yy << 1)) & 0x5555555555555555 + + // Combine x (even bits) and y (odd bits) + return xx | (yy << 1) } From 792a64bbcf8f091d68a8c7222896743dc9ab33b9 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 1 Apr 2025 22:01:43 +0300 Subject: [PATCH 099/196] fix bvh collision checks --- stdsystems/collision-detection-bvh.go | 50 ++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index cc50baf8..1cb50325 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -20,7 +20,6 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" - "image/color" "runtime" "sync" "time" @@ -104,28 +103,28 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } wg.Wait() - s.BvhTreeComponentManager.EachEntity(func(entity ecs.Entity) bool { - s.EntityManager.Delete(entity) - return true - }) - - for i := range s.trees { - tree := s.trees[i] - treeColor := color.RGBA{ - R: uint8(i * 255 / len(s.trees)), - G: uint8((i + 1) * 255 / len(s.trees)), - B: uint8((i + 2) * 255 / len(s.trees)), - A: 10, - } - tree.AabbNodes.AllData(func(aabb *stdcomponents.AABB) bool { - e := s.EntityManager.Create() - s.BvhTreeComponentManager.Create(e, stdcomponents.BvhTree{ - Color: treeColor, - }) - s.AABB.Create(e, *aabb) - return true - }) - } + //s.BvhTreeComponentManager.EachEntity(func(entity ecs.Entity) bool { + // s.EntityManager.Delete(entity) + // return true + //}) + // + //for i := range s.trees { + // tree := s.trees[i] + // treeColor := color.RGBA{ + // R: uint8(i * 255 / len(s.trees)), + // G: uint8((i + 1) * 255 / len(s.trees)), + // B: uint8((i + 2) * 255 / len(s.trees)), + // A: 10, + // } + // tree.AabbNodes.AllData(func(aabb *stdcomponents.AABB) bool { + // e := s.EntityManager.Create() + // s.BvhTreeComponentManager.Create(e, stdcomponents.BvhTree{ + // Color: treeColor, + // }) + // s.AABB.Create(e, *aabb) + // return true + // }) + //} if len(s.entities) < s.AABB.Len() { s.entities = make([]ecs.Entity, 0, s.AABB.Len()) @@ -204,6 +203,9 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity entityA := entities[i+startIndex] potentialEntities := s.broadPhase(entityA, make([]ecs.Entity, 0, 64)) + if len(potentialEntities) == 0 { + continue + } s.narrowPhase(entityA, potentialEntities, id) } }(startIndex, endIndex, workedId) @@ -277,8 +279,8 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE normal: transformB.Position.Sub(transformA.Position).Normalize(), depth: radiusA + radiusB - transformB.Position.Distance(transformA.Position), }) - continue } + continue } // GJK strategy From 66e4bad51664139b8a258da6732d97f4f601f53a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 1 Apr 2025 22:03:44 +0300 Subject: [PATCH 100/196] add leaf aabb check to bvh tree --- pkg/bvh/tree.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 1a700f23..7ce79c55 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -206,7 +206,10 @@ func (t *Tree) Query(aabb *stdcomponents.AABB, result []ecs.Entity) []ecs.Entity if node.childIndex <= 0 { // Is a leaf index := -int(node.childIndex) - result = append(result, t.leaves.Get(index).id) + leafAabb := t.AabbLeaves.GetValue(index) + if t.aabbOverlap(leafAabb, aabb) { + result = append(result, t.leaves.Get(index).id) + } continue } From ac4214cac7adf1465905bdde062975eef9153fe3 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 4 Apr 2025 08:47:26 +0300 Subject: [PATCH 101/196] upd --- examples/new-api/entities/bullet.go | 2 +- examples/new-api/systems/asterodd.go | 2 +- pkg/bvh/tree.go | 14 +++++++------- pkg/ecs/component-manager.go | 3 ++- stdsystems/collider.go | 6 +++--- stdsystems/collision-detection-bvh.go | 8 ++++---- stdsystems/sprite-matrix.go | 2 +- stdsystems/sprite.go | 2 +- 8 files changed, 20 insertions(+), 19 deletions(-) diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 30ae635c..0daf93d2 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -70,7 +70,7 @@ func CreateBullet( }, Layer: config.BulletCollisionLayer, Mask: 1< Date: Fri, 4 Apr 2025 16:13:57 +0300 Subject: [PATCH 102/196] fix collider.go cgo issue --- stdsystems/collider.go | 1 - taskfile.yml | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/stdsystems/collider.go b/stdsystems/collider.go index efe125a1..be98142f 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -14,7 +14,6 @@ Thank you for your support! package stdsystems -import "C" import ( "gomp/pkg/ecs" "gomp/stdcomponents" diff --git a/taskfile.yml b/taskfile.yml index 34559675..0e042bfd 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -2,6 +2,8 @@ version: "3" tasks: dev: + env: + CGO_ENABLED: 1 cmds: - go run ./examples/new-api/game.go From 33c80f31d0d84f0904120bfe81137c42961cfc28 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 5 Apr 2025 12:45:02 +0300 Subject: [PATCH 103/196] feat velocity system asserts --- stdsystems/velocity.go | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 4ec1ce80..f489f196 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -7,8 +7,10 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdsystems import ( + "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" + "math" "time" ) @@ -29,7 +31,10 @@ func (s *VelocitySystem) Run(dt time.Duration) { s.Velocities.EachEntity(func(e ecs.Entity) bool { velocity := s.Velocities.Get(e) + assert.True(s.isVelocityValid(velocity)) + position := s.Positions.Get(e) + assert.True(s.isPositionValid(position)) position.XY.X += velocity.X * dtSec position.XY.Y += velocity.Y * dtSec @@ -38,3 +43,51 @@ func (s *VelocitySystem) Run(dt time.Duration) { } func (s *VelocitySystem) Destroy() {} + +func (s *VelocitySystem) isVelocityValid(velocity *stdcomponents.Velocity) bool { + if velocity == nil { + return false + } + + // Convert to float64 + x := float64(velocity.X) + y := float64(velocity.Y) + + if math.IsInf(x, 1) || math.IsInf(x, -1) { + return false + } + + if math.IsInf(y, 1) || math.IsInf(y, -1) { + return false + } + + if math.IsNaN(x) || math.IsNaN(y) { + return false + } + + return true +} + +func (s *VelocitySystem) isPositionValid(position *stdcomponents.Position) bool { + if position == nil { + return false + } + + // Convert to float64 + x := float64(position.XY.X) + y := float64(position.XY.Y) + + if math.IsInf(x, 1) || math.IsInf(x, -1) { + return false + } + + if math.IsInf(y, 1) || math.IsInf(y, -1) { + return false + } + + if math.IsNaN(x) || math.IsNaN(y) { + return false + } + + return true +} From eebf4fe8680000f95865478d8c397d4f7b29c409 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 5 Apr 2025 12:45:47 +0300 Subject: [PATCH 104/196] feat debugTree const for collision-detection-bvh.go --- stdsystems/collision-detection-bvh.go | 49 +++++++++++++++------------ 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 584abf8a..64d5341b 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -20,11 +20,14 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" + "image/color" "runtime" "sync" "time" ) +const debugTree = true + var maxNumWorkers = runtime.NumCPU() - 1 func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { @@ -103,28 +106,30 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } wg.Wait() - //s.BvhTreeComponentManager.EachEntity(func(entity ecs.Entity) bool { - // s.EntityManager.Delete(entity) - // return true - //}) - // - //for i := range s.trees { - // tree := s.trees[i] - // treeColor := color.RGBA{ - // R: uint8(i * 255 / len(s.trees)), - // G: uint8((i + 1) * 255 / len(s.trees)), - // B: uint8((i + 2) * 255 / len(s.trees)), - // A: 10, - // } - // tree.AabbNodes.AllData(func(aabb *stdcomponents.AABB) bool { - // e := s.EntityManager.Create() - // s.BvhTreeComponentManager.Create(e, stdcomponents.BvhTree{ - // Color: treeColor, - // }) - // s.AABB.Create(e, *aabb) - // return true - // }) - //} + if debugTree { + s.BvhTreeComponentManager.EachEntity(func(entity ecs.Entity) bool { + s.EntityManager.Delete(entity) + return true + }) + + for i := range s.trees { + tree := s.trees[i] + treeColor := color.RGBA{ + R: uint8(i * 255 / len(s.trees)), + G: uint8((i + 1) * 255 / len(s.trees)), + B: uint8((i + 2) * 255 / len(s.trees)), + A: 30, + } + tree.AabbNodes.AllData(func(aabb *stdcomponents.AABB) bool { + e := s.EntityManager.Create() + s.BvhTreeComponentManager.Create(e, stdcomponents.BvhTree{ + Color: treeColor, + }) + s.AABB.Create(e, *aabb) + return true + }) + } + } if len(s.entities) < s.GenericCollider.Len() { s.entities = make([]ecs.Entity, 0, s.GenericCollider.Len()) From 2288801ccd87e5622905cb99707c8debe7d1d570 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 5 Apr 2025 12:48:08 +0300 Subject: [PATCH 105/196] feat bvh tree asserts --- pkg/bvh/tree.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 686f2aa3..6c8cc96a 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -15,9 +15,11 @@ Thank you for your support! package bvh import ( + "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" + "math" "math/bits" "slices" ) @@ -296,9 +298,13 @@ const mortonPrecision = (1 << 16) - 1 func (t *Tree) morton2D(aabb *stdcomponents.AABB) uint64 { center := aabb.Center() // Scale coordinates to 16-bit integers + assert.True(center.X >= 0 && center.Y >= 0, "morton2D: center out of range") - xx := uint64(center.X * mortonPrecision) - yy := uint64(center.Y * mortonPrecision) + xx := uint64(float64(center.X) * mortonPrecision) + yy := uint64(float64(center.Y) * mortonPrecision) + + assert.True(xx < math.MaxUint64, "morton2D: x out of range") + assert.True(yy < math.MaxUint64, "morton2D: y out of range") // Spread the bits of x into the even positions xx = (xx | (xx << 16)) & 0x0000FFFF0000FFFF From 0326177315404b7f54863a5593ef0b5ab263123a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 5 Apr 2025 12:57:41 +0300 Subject: [PATCH 106/196] update asterodd.go inits --- examples/new-api/entities/bullet.go | 2 +- examples/new-api/systems/asterodd.go | 31 +++++++++++++++++++++++++-- pkg/bvh/tree.go | 2 +- stdsystems/collision-detection-bvh.go | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 0daf93d2..30ae635c 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -70,7 +70,7 @@ func CreateBullet( }, Layer: config.BulletCollisionLayer, Mask: 1<= 0 && center.Y >= 0, "morton2D: center out of range") + //assert.True(center.X >= 0 && center.Y >= 0, "morton2D: center out of range") xx := uint64(float64(center.X) * mortonPrecision) yy := uint64(float64(center.Y) * mortonPrecision) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 64d5341b..c720ffe1 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -26,7 +26,7 @@ import ( "time" ) -const debugTree = true +const debugTree = false var maxNumWorkers = runtime.NumCPU() - 1 From b6228f277fcee3be28d2d6d6d98fb5e27a36bdf3 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 5 Apr 2025 18:40:06 +0300 Subject: [PATCH 107/196] init game refactor --- examples/new-api/game.go | 195 ++++++++-- examples/new-api/instances/system-list.go | 6 +- examples/new-api/main.go | 57 +++ examples/new-api/scenes/assterodd-scene.go | 94 +---- examples/new-api/scenes/main-scene.go | 17 +- game.go | 94 ----- pkg/ecs/world.go | 7 + scene.go | 3 +- stdsystems/render.go | 413 +++++++++++++++++++++ 9 files changed, 665 insertions(+), 221 deletions(-) create mode 100644 examples/new-api/main.go create mode 100644 stdsystems/render.go diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 80fa65b9..b21e86c3 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -15,47 +15,184 @@ Thank you for your support! package main import ( - "github.com/hajimehoshi/go-steamworks" - "golang.org/x/text/language" + "errors" "gomp" - "gomp/examples/new-api/scenes" - "os" + "gomp/examples/new-api/instances" + "gomp/pkg/ecs" + "gomp/stdsystems" + "time" ) -const appID = 12 // Rewrite this +func NewGame(initialScene gomp.AnyScene) Game { + game := Game{ + worlds: make([]instances.World, 0), + scenes: make([]gomp.AnyScene, 0), + lookup: make(map[gomp.SceneId]int), + renderSystem: stdsystems.NewRenderSystem(), + } + + game.LoadScene(initialScene) + game.SetActiveScene(initialScene.Id()) + + return game +} + +type Game struct { + // Scenes + worlds []instances.World + scenes []gomp.AnyScene + lookup map[gomp.SceneId]int + currentSceneId gomp.SceneId + + // Systems + renderSystem stdsystems.RenderSystem + + // Utils + shouldClose bool +} + +func (g *Game) Init() { + g.renderSystem.Init() + + for i := range g.scenes { + world := &g.worlds[i] + world.Init() -func initd() { - if steamworks.RestartAppIfNecessary(appID) { - os.Exit(1) + world.Systems.ColliderSystem.Init() + world.Systems.Velocity.Init() + world.Systems.CollisionDetectionBVH.Init() + world.Systems.CollisionResolution.Init() + world.Systems.AnimationSpriteMatrix.Init() + world.Systems.AnimationPlayer.Init() + world.Systems.SpriteMatrix.Init() + world.Systems.Sprite.Init() + world.Systems.YSort.Init() + world.Systems.Debug.Init() + world.Systems.AssetLib.Init() + world.Systems.Audio.Init() + world.Systems.SpatialAudio.Init() + + g.scenes[i].Init(world) + } +} + +func (g *Game) Update(dt time.Duration) { + for i := range g.scenes { + g.scenes[i].Update(dt) + } +} + +func (g *Game) FixedUpdate(dt time.Duration) { + for i := range g.worlds { + world := &g.worlds[i] + + world.Systems.ColliderSystem.Run(dt) + world.Systems.Velocity.Run(dt) + world.Systems.CollisionDetectionBVH.Run(dt) + world.Systems.CollisionResolution.Run(dt) + + g.scenes[i].FixedUpdate(dt) } - err := steamworks.Init() +} + +func (g *Game) Render(dt time.Duration) { + err := g.injectWorldToRender() if err != nil { - panic("steamworks.Init failed") + panic("jfdk") } + + id := g.lookup[g.currentSceneId] + world := &g.worlds[id] + + world.Systems.AnimationSpriteMatrix.Run() + world.Systems.AnimationPlayer.Run() + world.Systems.SpriteMatrix.Run() + world.Systems.Sprite.Run() + world.Systems.Debug.Run() + world.Systems.AssetLib.Run() + world.Systems.YSort.Run() + world.Systems.Audio.Run(dt) + world.Systems.SpatialAudio.Run(dt) + + scene := g.scenes[g.lookup[g.currentSceneId]] + scene.Render(dt) + + g.shouldClose = g.renderSystem.Run(dt) } -func SystemLang() language.Tag { - switch steamworks.SteamApps().GetCurrentGameLanguage() { - case "russian": - return language.Russian - case "english": - return language.English - case "japanese": - return language.Japanese +func (g *Game) Destroy() { + err := g.injectWorldToRender() + if err != nil { + panic("jfdk") + } + + for i := range g.scenes { + g.scenes[i].Destroy() + + world := &g.worlds[i] + world.Systems.Velocity.Destroy() + world.Systems.CollisionDetectionBVH.Destroy() + world.Systems.CollisionResolution.Destroy() + world.Systems.AnimationSpriteMatrix.Destroy() + world.Systems.AnimationPlayer.Destroy() + world.Systems.SpriteMatrix.Destroy() + world.Systems.Sprite.Destroy() + world.Systems.YSort.Destroy() + world.Systems.Debug.Destroy() + world.Systems.AssetLib.Destroy() + world.Systems.Audio.Destroy() + world.Systems.SpatialAudio.Destroy() + + world.Destroy() } - return language.Und + + g.renderSystem.Destroy() +} + +func (g *Game) ShouldDestroy() bool { + return g.shouldClose +} + +func (g *Game) LoadScene(scene gomp.AnyScene) { + g.scenes = append(g.scenes, scene) + g.worlds = append(g.worlds, ecs.NewWorld(instances.NewComponentList(), instances.NewSystemList())) + g.lookup[scene.Id()] = len(g.scenes) - 1 } -func main() { - //log.Println(SystemLang()) - sceneList := scenes.NewSceneList() +func (g *Game) SetActiveScene(id gomp.SceneId) { + g.currentSceneId = id +} + +func (g *Game) injectWorldToRender() error { + id, exists := g.lookup[g.currentSceneId] + if !exists { + return errors.New("scene not found") + } + + world := &g.worlds[id] - game := gomp.NewGame( - &sceneList.Main, - &sceneList.Assterodd, - ) - game.CurrentSceneId = scenes.AssteroddSceneId + g.renderSystem.InjectWorld( + &stdsystems.RenderInjector{ + EntityManager: &world.Entities, + RlTexturePros: &world.Components.RLTexturePro, + Positions: &world.Components.Position, + Rotations: &world.Components.Rotation, + Scales: &world.Components.Scale, + AnimationPlayers: &world.Components.AnimationPlayer, + Tints: &world.Components.Tint, + Flips: &world.Components.Flip, + Renderables: &world.Components.Renderable, + AnimationStates: &world.Components.AnimationState, + Sprites: &world.Components.Sprite, + SpriteMatrixes: &world.Components.SpriteMatrix, + RenderOrders: &world.Components.RenderOrder, + BoxColliders: &world.Components.ColliderBox, + CircleColliders: &world.Components.ColliderCircle, + AABBs: &world.Components.AABB, + Collisions: &world.Components.Collision, + ColliderSleepStateComponentManager: &world.Components.ColliderSleepState, + BvhTrees: &world.Components.BvhTree, + }) - engine := gomp.NewEngine(&game) - engine.Run(50, 0) + return nil } diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index c99c0de2..5b67dfb5 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -41,8 +41,7 @@ func NewSystemList() SystemList { ColliderSystem: stdsystems.NewColliderSystem(), CollisionResolution: stdsystems.NewCollisionResolutionSystem(), - RenderAssterodd: systems.NewRenderAssteroddSystem(), - RenderBogdan: systems.NewRenderBogdanSystem(), + RenderBogdan: systems.NewRenderBogdanSystem(), Audio: systems.NewAudioSystem(), SpatialAudio: systems.NewSpatialAudioSystem(), @@ -77,8 +76,7 @@ type SystemList struct { ColliderSystem stdsystems.ColliderSystem CollisionResolution stdsystems.CollisionResolutionSystem - RenderAssterodd systems.RenderAssteroddSystem - RenderBogdan systems.RenderBogdanSystem + RenderBogdan systems.RenderBogdanSystem Audio systems.AudioSystem SpatialAudio systems.SpatialAudioSystem diff --git a/examples/new-api/main.go b/examples/new-api/main.go new file mode 100644 index 00000000..07461cf1 --- /dev/null +++ b/examples/new-api/main.go @@ -0,0 +1,57 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "github.com/hajimehoshi/go-steamworks" + "golang.org/x/text/language" + "gomp" + "gomp/examples/new-api/scenes" + "os" +) + +const appID = 12 // Rewrite this + +func initd() { + if steamworks.RestartAppIfNecessary(appID) { + os.Exit(1) + } + err := steamworks.Init() + if err != nil { + panic("steamworks.Init failed") + } +} + +func SystemLang() language.Tag { + switch steamworks.SteamApps().GetCurrentGameLanguage() { + case "russian": + return language.Russian + case "english": + return language.English + case "japanese": + return language.Japanese + } + return language.Und +} + +func main() { + //log.Println(SystemLang()) + initialScene := scenes.NewAssteroddScene() + + game := NewGame(&initialScene) + + engine := gomp.NewEngine(&game) + engine.Run(50, 0) +} diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index c1b9d833..334687ea 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -57,118 +57,46 @@ COMPONENTS: */ func NewAssteroddScene() AssteroddScene { - return AssteroddScene{ - World: ecs.NewWorld(instances.NewComponentList(), instances.NewSystemList()), - } + return AssteroddScene{} } type AssteroddScene struct { - Game *gomp.Game - World instances.World + Game gomp.AnyGame + World *instances.World } -func (s *AssteroddScene) Id() gomp.SceneId { - return AssteroddSceneId -} +func (s *AssteroddScene) Init(world ecs.AnyWorld) { + s.World = world.(*instances.World) -func (s *AssteroddScene) Init() { - s.World.Init() - s.World.Systems.ColliderSystem.Init() - - // Scenes s.World.Systems.DampingSystem.Init() s.World.Systems.SpaceSpawner.Init() s.World.Systems.AssteroddSystem.Init() + s.World.Systems.CollisionHandler.Init() s.World.Systems.SpaceshipIntents.Init() - s.World.Systems.Velocity.Init() - - s.World.Systems.CollisionDetectionBVH.Init() - s.World.Systems.CollisionResolution.Init() - - // Animation - s.World.Systems.AnimationSpriteMatrix.Init() - s.World.Systems.AnimationPlayer.Init() - - s.World.Systems.SpriteMatrix.Init() - s.World.Systems.Sprite.Init() - s.World.Systems.YSort.Init() - - // RenderAssterodd - s.World.Systems.RenderAssterodd.Init() - s.World.Systems.Debug.Init() - s.World.Systems.AssetLib.Init() - s.World.Systems.Audio.Init() - s.World.Systems.SpatialAudio.Init() } func (s *AssteroddScene) Update(dt time.Duration) gomp.SceneId { - s.World.Systems.ColliderSystem.Run(dt) s.World.Systems.AssteroddSystem.Run(dt) - s.World.Systems.Audio.Run(dt) - s.World.Systems.SpatialAudio.Run(dt) return AssteroddSceneId } func (s *AssteroddScene) FixedUpdate(dt time.Duration) { s.World.Systems.SpaceshipIntents.Run(dt) - s.World.Systems.Velocity.Run(dt) s.World.Systems.DampingSystem.Run(dt) s.World.Systems.SpaceSpawner.Run(dt) - s.World.Systems.CollisionDetectionBVH.Run(dt) - s.World.Systems.CollisionResolution.Run(dt) s.World.Systems.CollisionHandler.Run(dt) s.World.Systems.Hp.Run(dt) } -func (s *AssteroddScene) Render(dt time.Duration) { - // Animation - s.World.Systems.AnimationSpriteMatrix.Run() - s.World.Systems.AnimationPlayer.Run() - - s.World.Systems.SpriteMatrix.Run() - s.World.Systems.Sprite.Run() - s.World.Systems.Debug.Run() - s.World.Systems.AssetLib.Run() - s.World.Systems.YSort.Run() - - shouldContinue := s.World.Systems.RenderAssterodd.Run(dt) - if !shouldContinue { - s.Game.SetShouldDestroy(true) - return - } -} +func (s *AssteroddScene) Render(dt time.Duration) {} func (s *AssteroddScene) Destroy() { - s.World.Destroy() - s.World.Systems.ColliderSystem.Destroy() - + s.World.Systems.DampingSystem.Destroy() s.World.Systems.SpaceSpawner.Destroy() - s.World.Systems.SpaceshipIntents.Destroy() - s.World.Systems.AssteroddSystem.Destroy() - - s.World.Systems.CollisionDetectionBVH.Destroy() - s.World.Systems.CollisionResolution.Destroy() s.World.Systems.CollisionHandler.Destroy() - s.World.Systems.Hp.Destroy() - s.World.Systems.Velocity.Destroy() - s.World.Systems.DampingSystem.Destroy() - - // Animation - s.World.Systems.AnimationSpriteMatrix.Destroy() - s.World.Systems.AnimationPlayer.Destroy() - - s.World.Systems.Sprite.Destroy() - s.World.Systems.SpriteMatrix.Destroy() - s.World.Systems.YSort.Destroy() - - // RenderAssterodd - s.World.Systems.Debug.Destroy() - s.World.Systems.AssetLib.Destroy() - s.World.Systems.RenderAssterodd.Destroy() - s.World.Systems.Audio.Destroy() - s.World.Systems.SpatialAudio.Destroy() + s.World.Systems.SpaceshipIntents.Destroy() } func (s *AssteroddScene) OnEnter() { @@ -179,4 +107,8 @@ func (s *AssteroddScene) OnExit() { } +func (s *AssteroddScene) Id() gomp.SceneId { + return AssteroddSceneId +} + var _ gomp.AnyScene = (*AssteroddScene)(nil) diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index bc5ae89c..fab7d840 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -22,22 +22,20 @@ import ( ) func NewMainScene() MainScene { - return MainScene{ - World: ecs.NewWorld(instances.NewComponentList(), instances.NewSystemList()), - } + return MainScene{} } type MainScene struct { - Game *gomp.Game - World instances.World + Game gomp.AnyGame + World *instances.World } func (s *MainScene) Id() gomp.SceneId { return MainSceneId } -func (s *MainScene) Init() { - s.World.Init() +func (s *MainScene) Init(world ecs.AnyWorld) { + s.World = world.(*instances.World) // Network receive s.World.Systems.Network.Init() @@ -93,11 +91,6 @@ func (s *MainScene) Render(dt time.Duration) { s.World.Systems.AssetLib.Run() s.World.Systems.YSort.Run() - shouldContinue := s.World.Systems.RenderBogdan.Run(dt) - if !shouldContinue { - s.Game.SetShouldDestroy(true) - return - } } func (s *MainScene) Destroy() { diff --git a/game.go b/game.go index ee2119c2..3518e8fa 100644 --- a/game.go +++ b/game.go @@ -15,8 +15,6 @@ Thank you for your support! package gomp import ( - "github.com/negrel/assert" - "reflect" "time" ) @@ -28,95 +26,3 @@ type AnyGame interface { Destroy() ShouldDestroy() bool } - -func NewGame(scenes ...AnyScene) Game { - sceneSet := make(map[SceneId]AnyScene, len(scenes)) - - for i := range len(scenes) { - id := scenes[i].Id() - - _, exists := sceneSet[id] - assert.False(exists, "Scene with id %d already exists. Duplicating ids?", id) - - sceneSet[id] = scenes[i] - } - - game := Game{ - Scenes: sceneSet, - RenderSystem: NewRenderSystem(), - } - - return game -} - -type Game struct { - Scenes map[SceneId]AnyScene - CurrentSceneId SceneId - - shouldDestroy bool - RenderSystem RenderSystem -} - -func (g *Game) Init() { - for _, scene := range g.Scenes { - g.injectToScene(scene) - } - - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - g.RenderSystem.Init() - scene.Init() -} - -func (g *Game) Update(dt time.Duration) { - // Scenes - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - g.CurrentSceneId = scene.Update(dt) -} - -func (g *Game) FixedUpdate(dt time.Duration) { - // Scenes - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - scene.FixedUpdate(dt) -} - -func (g *Game) Render(dt time.Duration) { - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - scene.Render(dt) -} - -func (g *Game) Destroy() { - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - scene.Destroy() - g.RenderSystem.Destroy() -} - -func (g *Game) ShouldDestroy() bool { - return g.shouldDestroy -} - -func (g *Game) SetShouldDestroy(value bool) { - g.shouldDestroy = value -} - -func (g *Game) injectToScene(scene AnyScene) { - reflectedScene := reflect.ValueOf(scene).Elem() - sceneLen := reflectedScene.NumField() - - reflectedGame := reflect.ValueOf(g) - gameType := reflect.TypeOf(g) - - for i := 0; i < sceneLen; i++ { - field := reflectedScene.Field(i) - fieldType := field.Type() - - if fieldType == gameType { - reflectedScene.Field(i).Set(reflectedGame) - continue - } - } -} diff --git a/pkg/ecs/world.go b/pkg/ecs/world.go index fabcc46a..4b45fdf8 100644 --- a/pkg/ecs/world.go +++ b/pkg/ecs/world.go @@ -18,6 +18,13 @@ import ( "reflect" ) +type AnyWorld interface { + Init() + Destroy() + injectEntityManagerToComponents() + injectComponentsToSystems() +} + type World[C, S any] struct { Entities EntityManager Components C diff --git a/scene.go b/scene.go index 012b9dda..ffde367a 100644 --- a/scene.go +++ b/scene.go @@ -15,13 +15,14 @@ Thank you for your support! package gomp import ( + "gomp/pkg/ecs" "time" ) type SceneId uint16 type AnyScene interface { - Init() + Init(world ecs.AnyWorld) Update(dt time.Duration) SceneId FixedUpdate(dt time.Duration) Render(dt time.Duration) diff --git a/stdsystems/render.go b/stdsystems/render.go new file mode 100644 index 00000000..ffab1536 --- /dev/null +++ b/stdsystems/render.go @@ -0,0 +1,413 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +<- HromRu Donated 1 500 RUB + +Thank you for your support! +*/ + +package stdsystems + +import ( + "fmt" + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "math" + "slices" + "sync" + "time" +) + +func NewRenderSystem() RenderSystem { + return RenderSystem{ + instanceData: make([]stdcomponents.RLTexturePro, 0, 8192), + } +} + +type RenderSystem struct { + EntityManager *ecs.EntityManager + RlTexturePros *stdcomponents.RLTextureProComponentManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + Tints *stdcomponents.TintComponentManager + Flips *stdcomponents.FlipComponentManager + Renderables *stdcomponents.RenderableComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + Sprites *stdcomponents.SpriteComponentManager + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + AABBs *stdcomponents.AABBComponentManager + Collisions *stdcomponents.CollisionComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + BvhTrees *stdcomponents.BvhTreeComponentManager + + renderList []renderEntry + instanceData []stdcomponents.RLTexturePro + camera rl.Camera2D + + monitorWidth int + monitorHeight int + + debug bool +} + +type renderEntry struct { + Entity ecs.Entity + TextureId int + ZIndex float32 +} + +func (s *RenderSystem) Init() { + rl.InitWindow(1280, 720, "GOMP") + s.monitorWidth = rl.GetScreenWidth() + s.monitorHeight = rl.GetScreenHeight() + s.camera = rl.Camera2D{ + Target: rl.NewVector2(float32(s.monitorWidth/2), float32(s.monitorHeight/2)), + Offset: rl.NewVector2(float32(s.monitorWidth/2), float32(s.monitorHeight/2)), + Rotation: 0, + Zoom: 1, + } +} + +func (s *RenderSystem) Run(dt time.Duration) bool { + if rl.WindowShouldClose() { + return true + } + + if rl.IsKeyPressed(rl.KeyF12) { + s.debug = !s.debug + } + + s.prepareRender(dt) + + rl.BeginDrawing() + rl.ClearBackground(rl.Black) + s.render() + + rl.DrawFPS(10, 10) + rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) + rl.EndDrawing() + + return false +} + +func (s *RenderSystem) Destroy() { + rl.CloseWindow() +} + +type RenderInjector struct { + EntityManager *ecs.EntityManager + RlTexturePros *stdcomponents.RLTextureProComponentManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + Tints *stdcomponents.TintComponentManager + Flips *stdcomponents.FlipComponentManager + Renderables *stdcomponents.RenderableComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + Sprites *stdcomponents.SpriteComponentManager + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + AABBs *stdcomponents.AABBComponentManager + Collisions *stdcomponents.CollisionComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + BvhTrees *stdcomponents.BvhTreeComponentManager +} + +func (s *RenderSystem) InjectWorld(injector *RenderInjector) { + s.EntityManager = injector.EntityManager + s.RlTexturePros = injector.RlTexturePros + s.Positions = injector.Positions + s.Rotations = injector.Rotations + s.Scales = injector.Scales + s.AnimationPlayers = injector.AnimationPlayers + s.Tints = injector.Tints + s.Flips = injector.Flips + s.Renderables = injector.Renderables + s.AnimationStates = injector.AnimationStates + s.Sprites = injector.Sprites + s.SpriteMatrixes = injector.SpriteMatrixes + s.RenderOrders = injector.RenderOrders + s.BoxColliders = injector.BoxColliders + s.CircleColliders = injector.CircleColliders + s.AABBs = injector.AABBs + s.Collisions = injector.Collisions + s.ColliderSleepStateComponentManager = injector.ColliderSleepStateComponentManager + s.BvhTrees = injector.BvhTrees +} + +func (s *RenderSystem) render() { + // ========== + // DEBUG + // ========== + if s.debug { + rl.BeginMode2D(s.camera) + s.BoxColliders.EachEntity(func(e ecs.Entity) bool { + col := s.BoxColliders.Get(e) + scale := s.Scales.Get(e) + pos := s.Positions.Get(e) + rot := s.Rotations.Get(e) + + rl.DrawRectanglePro(rl.Rectangle{ + X: pos.XY.X, + Y: pos.XY.Y, + Width: col.WH.X * scale.XY.X, + Height: col.WH.Y * scale.XY.Y, + }, rl.Vector2{ + X: col.Offset.X * scale.XY.X, + Y: col.Offset.Y * scale.XY.Y, + }, float32(rot.Degrees()), rl.DarkGreen) + return true + }) + s.CircleColliders.EachEntity(func(e ecs.Entity) bool { + col := s.CircleColliders.Get(e) + scale := s.Scales.Get(e) + pos := s.Positions.Get(e) + + color := rl.DarkGreen + isSleeping := s.ColliderSleepStateComponentManager.Get(e) + if isSleeping != nil { + color = rl.Blue + } + + posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) + rl.DrawCircle(int32(posWithOffset.X), int32(posWithOffset.Y), col.Radius*scale.XY.X, color) + return true + }) + rl.EndMode2D() + } + + // Extract and sort entities + if cap(s.renderList) < s.Renderables.Len() { + s.renderList = append(s.renderList, make([]renderEntry, 0, s.Renderables.Len()-cap(s.renderList))...) + } + s.Renderables.EachEntity(func(e ecs.Entity) bool { + renderOrder := s.RenderOrders.Get(e) + + spriteMatrix := s.SpriteMatrixes.Get(e) + if spriteMatrix != nil { + s.renderList = append(s.renderList, renderEntry{ + Entity: e, + TextureId: int(spriteMatrix.Texture.ID), + ZIndex: renderOrder.CalculatedZ, + }) + return true + } + + sprite := s.Sprites.Get(e) + if sprite != nil { + s.renderList = append(s.renderList, renderEntry{ + Entity: e, + TextureId: int(sprite.Texture.ID), + ZIndex: renderOrder.CalculatedZ, + }) + return true + } + + panic("Unknown renderable type") + }) + + slices.SortStableFunc(s.renderList, func(a, b renderEntry) int { + if a.TextureId == b.TextureId { + return int(math.Floor(float64(a.ZIndex - b.ZIndex))) + } + return int(a.TextureId - b.TextureId) + }) + + // Batch and render + var currentTex = -1 + for i := range s.renderList { + entry := &s.renderList[i] + if entry.TextureId != currentTex || len(s.instanceData) >= 8192 { + if len(s.instanceData) > 0 { + s.submitBatch(s.instanceData) + s.instanceData = s.instanceData[:0] + } + currentTex = entry.TextureId + } + s.instanceData = append(s.instanceData, s.getInstanceData(entry.Entity)) + } + s.submitBatch(s.instanceData) // Submit last batch + s.renderList = s.renderList[:0] + + // ========== + // DEBUG + // ========== + if s.debug { + rl.BeginMode2D(s.camera) + s.AABBs.EachEntity(func(e ecs.Entity) bool { + aabb := s.AABBs.Get(e) + clr := rl.Green + isSleeping := s.ColliderSleepStateComponentManager.Get(e) + if isSleeping != nil { + clr = rl.Blue + } + isTree := s.BvhTrees.Get(e) + if isTree != nil { + rl.DrawRectangle(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), isTree.Color) + return true + } + rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), clr) + return true + }) + s.Collisions.EachEntity(func(entity ecs.Entity) bool { + pos := s.Positions.Get(entity) + rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) + return true + }) + rl.EndMode2D() + } +} + +func (s *RenderSystem) submitBatch(data []stdcomponents.RLTexturePro) { + rl.BeginMode2D(s.camera) + if s.debug { + for i := range data { + rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) + rl.DrawRectangle(int32(data[i].Dest.X-2), int32(data[i].Dest.Y-2), 4, 4, rl.Red) + } + } else { + for i := range data { + rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) + } + } + rl.EndMode2D() +} + +func (s *RenderSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { + return *s.RlTexturePros.Get(e) +} + +func (s *RenderSystem) prepareRender(dt time.Duration) { + wg := new(sync.WaitGroup) + wg.Add(6) + s.prepareAnimations(wg) + go s.prepareFlips(wg) + go s.preparePositions(wg, dt) + go s.prepareRotations(wg) + go s.prepareScales(wg) + go s.prepareTints(wg) + wg.Wait() +} + +func (s *RenderSystem) prepareAnimations(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + animation := s.AnimationPlayers.Get(entity) + if animation == nil { + return true + } + frame := &texturePro.Frame + if animation.Vertical { + frame.Y += frame.Height * float32(animation.Current) + } else { + frame.X += frame.Width * float32(animation.Current) + } + return true + }) +} + +func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + flipped := s.Flips.Get(entity) + if flipped == nil { + return true + } + if flipped.X { + texturePro.Frame.Width *= -1 + } + if flipped.Y { + texturePro.Frame.Height *= -1 + } + return true + }) +} + +func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { + defer wg.Done() + dts := dt.Seconds() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + position := s.Positions.Get(entity) + if position == nil { + return true + } + decay := 40.0 // DECAY IS TICKRATE DEPENDENT + x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) + y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) + texturePro.Dest.X = x + texturePro.Dest.Y = y + //player := s.Player.Get(entity) + //if player != nil { + // s.camera.Target.X = x + // s.camera.Target.Y = y + //} + + return true + }) +} + +func (s *RenderSystem) prepareRotations(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + rotation := s.Rotations.Get(entity) + if rotation == nil { + return true + } + texturePro.Rotation = float32(rotation.Degrees()) + return true + }) +} + +func (s *RenderSystem) prepareScales(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + scale := s.Scales.Get(entity) + if scale == nil { + return true + } + texturePro.Dest.Width *= scale.XY.X + texturePro.Dest.Height *= scale.XY.Y + return true + }) +} + +func (s *RenderSystem) prepareTints(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + tr := s.RlTexturePros.Get(entity) + tint := s.Tints.Get(entity) + if tint == nil { + return true + } + trTint := &tr.Tint + trTint.A = tint.A + trTint.R = tint.R + trTint.G = tint.G + trTint.B = tint.B + return true + }) +} + +func (s *RenderSystem) expDecay(a, b, decay, dt float64) float64 { + return b + (a-b)*(math.Exp(-decay*dt)) +} From 77cc9196eb302d64c42b303cafe30c74bf60309b Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 6 Apr 2025 12:16:11 +0300 Subject: [PATCH 108/196] refactor world to render injection --- examples/new-api/game.go | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index b21e86c3..596c7f0c 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -15,7 +15,6 @@ Thank you for your support! package main import ( - "errors" "gomp" "gomp/examples/new-api/instances" "gomp/pkg/ecs" @@ -96,11 +95,6 @@ func (g *Game) FixedUpdate(dt time.Duration) { } func (g *Game) Render(dt time.Duration) { - err := g.injectWorldToRender() - if err != nil { - panic("jfdk") - } - id := g.lookup[g.currentSceneId] world := &g.worlds[id] @@ -121,11 +115,6 @@ func (g *Game) Render(dt time.Duration) { } func (g *Game) Destroy() { - err := g.injectWorldToRender() - if err != nil { - panic("jfdk") - } - for i := range g.scenes { g.scenes[i].Destroy() @@ -161,16 +150,9 @@ func (g *Game) LoadScene(scene gomp.AnyScene) { func (g *Game) SetActiveScene(id gomp.SceneId) { g.currentSceneId = id -} - -func (g *Game) injectWorldToRender() error { - id, exists := g.lookup[g.currentSceneId] - if !exists { - return errors.New("scene not found") - } - - world := &g.worlds[id] + // Inject active scene world to render system + world := &g.worlds[g.lookup[g.currentSceneId]] g.renderSystem.InjectWorld( &stdsystems.RenderInjector{ EntityManager: &world.Entities, @@ -193,6 +175,4 @@ func (g *Game) injectWorldToRender() error { ColliderSleepStateComponentManager: &world.Components.ColliderSleepState, BvhTrees: &world.Components.BvhTree, }) - - return nil } From 7b1eddec061c0d49959b23531c48cca2db398925 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 6 Apr 2025 13:19:40 +0300 Subject: [PATCH 109/196] ftm game.go --- examples/new-api/game.go | 176 +++++++++++++++++++++------------------ 1 file changed, 93 insertions(+), 83 deletions(-) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 596c7f0c..0e4bdf42 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -23,7 +23,7 @@ import ( ) func NewGame(initialScene gomp.AnyScene) Game { - game := Game{ + var game = Game{ worlds: make([]instances.World, 0), scenes: make([]gomp.AnyScene, 0), lookup: make(map[gomp.SceneId]int), @@ -37,41 +37,44 @@ func NewGame(initialScene gomp.AnyScene) Game { } type Game struct { - // Scenes - worlds []instances.World - scenes []gomp.AnyScene - lookup map[gomp.SceneId]int - currentSceneId gomp.SceneId + // Scene manager + worlds []instances.World + scenes []gomp.AnyScene + lookup map[gomp.SceneId]int // Systems renderSystem stdsystems.RenderSystem // Utils - shouldClose bool + shouldClose bool + currentSceneId gomp.SceneId } func (g *Game) Init() { g.renderSystem.Init() for i := range g.scenes { - world := &g.worlds[i] + var scene = g.scenes[i] + var world = &g.worlds[i] + var systems = &world.Systems + world.Init() - world.Systems.ColliderSystem.Init() - world.Systems.Velocity.Init() - world.Systems.CollisionDetectionBVH.Init() - world.Systems.CollisionResolution.Init() - world.Systems.AnimationSpriteMatrix.Init() - world.Systems.AnimationPlayer.Init() - world.Systems.SpriteMatrix.Init() - world.Systems.Sprite.Init() - world.Systems.YSort.Init() - world.Systems.Debug.Init() - world.Systems.AssetLib.Init() - world.Systems.Audio.Init() - world.Systems.SpatialAudio.Init() - - g.scenes[i].Init(world) + systems.ColliderSystem.Init() + systems.Velocity.Init() + systems.CollisionDetectionBVH.Init() + systems.CollisionResolution.Init() + systems.AnimationSpriteMatrix.Init() + systems.AnimationPlayer.Init() + systems.SpriteMatrix.Init() + systems.Sprite.Init() + systems.YSort.Init() + systems.Debug.Init() + systems.AssetLib.Init() + systems.Audio.Init() + systems.SpatialAudio.Init() + + scene.Init(world) } } @@ -83,54 +86,59 @@ func (g *Game) Update(dt time.Duration) { func (g *Game) FixedUpdate(dt time.Duration) { for i := range g.worlds { - world := &g.worlds[i] + var world = &g.worlds[i] + var scene = g.scenes[i] + var systems = &world.Systems - world.Systems.ColliderSystem.Run(dt) - world.Systems.Velocity.Run(dt) - world.Systems.CollisionDetectionBVH.Run(dt) - world.Systems.CollisionResolution.Run(dt) + systems.ColliderSystem.Run(dt) + systems.Velocity.Run(dt) + systems.CollisionDetectionBVH.Run(dt) + systems.CollisionResolution.Run(dt) - g.scenes[i].FixedUpdate(dt) + scene.FixedUpdate(dt) } } func (g *Game) Render(dt time.Duration) { - id := g.lookup[g.currentSceneId] - world := &g.worlds[id] - - world.Systems.AnimationSpriteMatrix.Run() - world.Systems.AnimationPlayer.Run() - world.Systems.SpriteMatrix.Run() - world.Systems.Sprite.Run() - world.Systems.Debug.Run() - world.Systems.AssetLib.Run() - world.Systems.YSort.Run() - world.Systems.Audio.Run(dt) - world.Systems.SpatialAudio.Run(dt) - - scene := g.scenes[g.lookup[g.currentSceneId]] - scene.Render(dt) + var id = g.lookup[g.currentSceneId] + var scene = g.scenes[id] + var systems = &g.worlds[id].Systems + + systems.AnimationSpriteMatrix.Run() + systems.AnimationPlayer.Run() + systems.SpriteMatrix.Run() + systems.Sprite.Run() + systems.Debug.Run() + systems.AssetLib.Run() + systems.YSort.Run() + systems.Audio.Run(dt) + systems.SpatialAudio.Run(dt) g.shouldClose = g.renderSystem.Run(dt) + + scene.Render(dt) } func (g *Game) Destroy() { - for i := range g.scenes { - g.scenes[i].Destroy() - - world := &g.worlds[i] - world.Systems.Velocity.Destroy() - world.Systems.CollisionDetectionBVH.Destroy() - world.Systems.CollisionResolution.Destroy() - world.Systems.AnimationSpriteMatrix.Destroy() - world.Systems.AnimationPlayer.Destroy() - world.Systems.SpriteMatrix.Destroy() - world.Systems.Sprite.Destroy() - world.Systems.YSort.Destroy() - world.Systems.Debug.Destroy() - world.Systems.AssetLib.Destroy() - world.Systems.Audio.Destroy() - world.Systems.SpatialAudio.Destroy() + for i := range g.worlds { + var scene = g.scenes[i] + var world = &g.worlds[i] + var systems = &world.Systems + + scene.Destroy() + + systems.Velocity.Destroy() + systems.CollisionDetectionBVH.Destroy() + systems.CollisionResolution.Destroy() + systems.AnimationSpriteMatrix.Destroy() + systems.AnimationPlayer.Destroy() + systems.SpriteMatrix.Destroy() + systems.Sprite.Destroy() + systems.YSort.Destroy() + systems.Debug.Destroy() + systems.AssetLib.Destroy() + systems.Audio.Destroy() + systems.SpatialAudio.Destroy() world.Destroy() } @@ -138,10 +146,6 @@ func (g *Game) Destroy() { g.renderSystem.Destroy() } -func (g *Game) ShouldDestroy() bool { - return g.shouldClose -} - func (g *Game) LoadScene(scene gomp.AnyScene) { g.scenes = append(g.scenes, scene) g.worlds = append(g.worlds, ecs.NewWorld(instances.NewComponentList(), instances.NewSystemList())) @@ -152,27 +156,33 @@ func (g *Game) SetActiveScene(id gomp.SceneId) { g.currentSceneId = id // Inject active scene world to render system - world := &g.worlds[g.lookup[g.currentSceneId]] + var world = &g.worlds[g.lookup[id]] + var components = &world.Components + g.renderSystem.InjectWorld( &stdsystems.RenderInjector{ EntityManager: &world.Entities, - RlTexturePros: &world.Components.RLTexturePro, - Positions: &world.Components.Position, - Rotations: &world.Components.Rotation, - Scales: &world.Components.Scale, - AnimationPlayers: &world.Components.AnimationPlayer, - Tints: &world.Components.Tint, - Flips: &world.Components.Flip, - Renderables: &world.Components.Renderable, - AnimationStates: &world.Components.AnimationState, - Sprites: &world.Components.Sprite, - SpriteMatrixes: &world.Components.SpriteMatrix, - RenderOrders: &world.Components.RenderOrder, - BoxColliders: &world.Components.ColliderBox, - CircleColliders: &world.Components.ColliderCircle, - AABBs: &world.Components.AABB, - Collisions: &world.Components.Collision, - ColliderSleepStateComponentManager: &world.Components.ColliderSleepState, - BvhTrees: &world.Components.BvhTree, + RlTexturePros: &components.RLTexturePro, + Positions: &components.Position, + Rotations: &components.Rotation, + Scales: &components.Scale, + AnimationPlayers: &components.AnimationPlayer, + Tints: &components.Tint, + Flips: &components.Flip, + Renderables: &components.Renderable, + AnimationStates: &components.AnimationState, + Sprites: &components.Sprite, + SpriteMatrixes: &components.SpriteMatrix, + RenderOrders: &components.RenderOrder, + BoxColliders: &components.ColliderBox, + CircleColliders: &components.ColliderCircle, + AABBs: &components.AABB, + Collisions: &components.Collision, + ColliderSleepStateComponentManager: &components.ColliderSleepState, + BvhTrees: &components.BvhTree, }) } + +func (g *Game) ShouldDestroy() bool { + return g.shouldClose +} From 3aafeb4d0d7428b102efac5b59743d92459f9d3e Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 6 Apr 2025 13:23:15 +0300 Subject: [PATCH 110/196] fix fixUpdate rapid effect after fps drop --- engine.go | 1 + 1 file changed, 1 insertion(+) diff --git a/engine.go b/engine.go index 7a29b6e8..c932b18d 100644 --- a/engine.go +++ b/engine.go @@ -72,6 +72,7 @@ func (e *Engine) Run(tickrate uint, framerate uint) { loops++ } if loops >= MaxFrameSkips { + nextFixedUpdateAt = time.Now() log.Println("Too many updates detected") } From 80d576af421fa80e0d9eee4b9128e74fa19a35d8 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 6 Apr 2025 23:38:37 +0300 Subject: [PATCH 111/196] init grid and bvh component --- render.go | 43 ------- stdcomponents/bvh-tree.go | 148 ++++++++++++++++++++++++ stdcomponents/collision-grid.go | 46 ++++++++ stdcomponents/ids.go | 4 +- stdcomponents/tree-bvh.go | 30 ----- stdsystems/bvh-tree.go | 196 ++++++++++++++++++++++++++++++++ 6 files changed, 393 insertions(+), 74 deletions(-) delete mode 100644 render.go create mode 100644 stdcomponents/bvh-tree.go create mode 100644 stdcomponents/collision-grid.go delete mode 100644 stdcomponents/tree-bvh.go create mode 100644 stdsystems/bvh-tree.go diff --git a/render.go b/render.go deleted file mode 100644 index d0fc1e9a..00000000 --- a/render.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package gomp - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "time" -) - -func NewRenderSystem() RenderSystem { - return RenderSystem{} -} - -type RenderSystem struct{} - -func (s *RenderSystem) Init() { - //monitor := rl.GetCurrentMonitor() - //width, height := rl.GetMonitorWidth(monitor), rl.GetMonitorHeight(monitor) - rl.InitWindow(1280, 720, "raylib [core] ebiten-ecs - basic window") - //rl.SetWindowState(rl.FlagFullscreenMode) -} -func (s *RenderSystem) Run(dt time.Duration) bool { - if rl.WindowShouldClose() { - return false - } - return true -} - -func (s *RenderSystem) Destroy() { - rl.CloseWindow() -} diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go new file mode 100644 index 00000000..60ceafa9 --- /dev/null +++ b/stdcomponents/bvh-tree.go @@ -0,0 +1,148 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "github.com/negrel/assert" + "gomp/pkg/ecs" + "math" +) + +const mortonPrecision = (1 << 16) - 1 + +type BvhNode struct { + ChildIndex int32 // if < 0 then points to BvhLeaf +} + +type BvhLeaf struct { + Id ecs.Entity +} + +type BvhComponent struct { + Entity ecs.Entity + Aabb AABB + Code uint64 +} + +type BvhTree struct { + Nodes ecs.PagedArray[BvhNode] + AabbNodes ecs.PagedArray[AABB] + Leaves ecs.PagedArray[BvhLeaf] + AabbLeaves ecs.PagedArray[AABB] + Codes ecs.PagedArray[uint64] + Components ecs.PagedArray[BvhComponent] + + ComponentsSlice []BvhComponent +} + +func (t *BvhTree) AddComponent(entity ecs.Entity, aabb AABB) { + code := t.morton2D(&aabb) + t.Components.Append(BvhComponent{ + Entity: entity, + Aabb: aabb, + Code: code, + }) +} + +func (t *BvhTree) Query(aabb *AABB, result []ecs.Entity) []ecs.Entity { + if t.Nodes.Len() == 0 { // Handle empty tree + return result + } + + // Use stack-based traversal + const stackSize = 32 + stack := [stackSize]int32{0} + stackLen := 1 + + for stackLen > 0 { + stackLen-- + nodeIndex := int(stack[stackLen]) + a := t.AabbNodes.Get(nodeIndex) + b := aabb + + // Early exit if no AABB overlap + if !t.aabbOverlap(a, b) { + continue + } + + node := t.Nodes.Get(nodeIndex) + if node.ChildIndex <= 0 { + // Is a leaf + index := -int(node.ChildIndex) + leafAabb := t.AabbLeaves.Get(index) + if t.aabbOverlap(leafAabb, aabb) { + result = append(result, t.Leaves.Get(index).Id) + } + continue + } + + // Push child indices (right and left) onto the stack. + stack[stackLen] = node.ChildIndex + 1 + stack[stackLen+1] = node.ChildIndex + stackLen += 2 + } + + return result +} + +// go:inline aabbOverlap checks if two AABB intersect +func (t *BvhTree) aabbOverlap(a, b *AABB) bool { + return a.Max.X >= b.Min.X && a.Min.X <= b.Max.X && + a.Max.Y >= b.Min.Y && a.Min.Y <= b.Max.Y +} + +// Expands a 16-bit integer into 32 bits by inserting 1 zero after each bit +func (t *BvhTree) expandBits2D(v uint32) uint32 { + v = (v | (v << 8)) & 0x00FF00FF + v = (v | (v << 4)) & 0x0F0F0F0F + v = (v | (v << 2)) & 0x33333333 + v = (v | (v << 1)) & 0x55555555 + return v +} + +func (t *BvhTree) morton2D(aabb *AABB) uint64 { + center := aabb.Center() + // Scale coordinates to 16-bit integers + //assert.True(center.X >= 0 && center.Y >= 0, "morton2D: center out of range") + + xx := uint64(float64(center.X) * mortonPrecision) + yy := uint64(float64(center.Y) * mortonPrecision) + + assert.True(xx < math.MaxUint64, "morton2D: x out of range") + assert.True(yy < math.MaxUint64, "morton2D: y out of range") + + // Spread the bits of x into the even positions + xx = (xx | (xx << 16)) & 0x0000FFFF0000FFFF + xx = (xx | (xx << 8)) & 0x00FF00FF00FF00FF + xx = (xx | (xx << 4)) & 0x0F0F0F0F0F0F0F0F + xx = (xx | (xx << 2)) & 0x3333333333333333 + xx = (xx | (xx << 1)) & 0x5555555555555555 + + // Spread the bits of y into the even positions and shift to odd positions + yy = (yy | (yy << 16)) & 0x0000FFFF0000FFFF + yy = (yy | (yy << 8)) & 0x00FF00FF00FF00FF + yy = (yy | (yy << 4)) & 0x0F0F0F0F0F0F0F0F + yy = (yy | (yy << 2)) & 0x3333333333333333 + yy = (yy | (yy << 1)) & 0x5555555555555555 + + // Combine x (even bits) and y (odd bits) + return xx | (yy << 1) +} + +type BvhTreeComponentManager = ecs.ComponentManager[BvhTree] + +func NewBvhTreeComponentManager() BvhTreeComponentManager { + return ecs.NewComponentManager[BvhTree](BvhTreeComponentId) +} diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go new file mode 100644 index 00000000..6da5cf43 --- /dev/null +++ b/stdcomponents/collision-grid.go @@ -0,0 +1,46 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" + "gomp/vectors" +) + +type CollisionGrid struct { + Layer CollisionLayer // Layer of the grid + Entities ecs.PagedArray[ecs.Entity] // List of Entities in the grid + CellLookup map[SpatialIndex]ecs.Entity // Pointer to cell + + MinBounds AABB // Bounds of the smallest entity in the grid + CellSize vectors.Vec2 +} + +func (t *CollisionGrid) RegisterEntity(entity ecs.Entity, aabb *AABB) { + t.Entities.Append(entity) + + if aabb.Min.X < t.MinBounds.Min.X { + t.MinBounds.Min.X = aabb.Min.X + } + if aabb.Min.Y < t.MinBounds.Min.Y { + t.MinBounds.Min.Y = aabb.Min.Y + } +} + +type CollisionGridComponentManager = ecs.ComponentManager[CollisionGrid] + +func NewCollisionGridComponentManager() CollisionGridComponentManager { + return ecs.NewComponentManager[CollisionGrid](CollisionGridComponentId) +} diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 71463f75..c2e62791 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -40,5 +40,7 @@ const ( AABBComponentId RigidBodyComponentId BvhTreeComponentId - StdComponentIds + CollisionGridComponentId + + StdComponentIds // StdComponentIds MUST always be the last ) diff --git a/stdcomponents/tree-bvh.go b/stdcomponents/tree-bvh.go deleted file mode 100644 index e15e122f..00000000 --- a/stdcomponents/tree-bvh.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package stdcomponents - -import ( - "gomp/pkg/ecs" - "image/color" -) - -type BvhTree struct { - Color color.RGBA -} - -type BvhTreeComponentManager = ecs.ComponentManager[BvhTree] - -func NewBvhTreeComponentManager() BvhTreeComponentManager { - return ecs.NewComponentManager[BvhTree](BvhTreeComponentId) -} diff --git a/stdsystems/bvh-tree.go b/stdsystems/bvh-tree.go new file mode 100644 index 00000000..7c52f496 --- /dev/null +++ b/stdsystems/bvh-tree.go @@ -0,0 +1,196 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "math/bits" + "slices" + "time" +) + +func NewBvhTreeSystem() BvhTreeSystem { + return BvhTreeSystem{} +} + +type BvhTreeSystem struct { + EntityManager *ecs.EntityManager +} + +func (s *BvhTreeSystem) Init() {} +func (s *BvhTreeSystem) Run(dt time.Duration) {} +func (s *BvhTreeSystem) Destroy() {} + +func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { + // Reset tree + t.Nodes.Reset() + t.AabbNodes.Reset() + t.Leaves.Reset() + t.AabbLeaves.Reset() + t.Codes.Reset() + + // Extract and sort components by morton code + if cap(t.ComponentsSlice) < t.Components.Len() { + t.ComponentsSlice = make([]stdcomponents.BvhComponent, 0, t.Components.Len()) + } + + t.ComponentsSlice = t.Components.Raw(t.ComponentsSlice) + + slices.SortFunc(t.ComponentsSlice, func(a, b stdcomponents.BvhComponent) int { + return int(a.Code - b.Code) + }) + + // Add leaves + for i := range t.ComponentsSlice { + component := &t.ComponentsSlice[i] + t.Leaves.Append(stdcomponents.BvhLeaf{Id: component.Entity}) + t.AabbLeaves.Append(component.Aabb) + t.Codes.Append(component.Code) + } + t.Components.Reset() + + if t.Leaves.Len() == 0 { + return + } + + // Add root node + t.Nodes.Append(stdcomponents.BvhNode{-1}) + t.AabbNodes.Append(stdcomponents.AABB{}) + + type buildTask struct { + parentIndex int + start int + end int + childrenCreated bool + } + + stack := []buildTask{ + {parentIndex: 0, start: 0, end: t.Leaves.Len() - 1, childrenCreated: false}, + } + + for len(stack) > 0 { + // Pop the last task + task := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + if !task.childrenCreated { + if task.start == task.end { + // Leaf node + t.Nodes.Get(task.parentIndex).ChildIndex = -int32(task.start) + t.AabbNodes.Set(task.parentIndex, t.AabbLeaves.GetValue(task.start)) + continue + } + + split := s.findSplit(t, task.start, task.end) + + // Create left and right nodes + leftIndex := t.Nodes.Len() + t.Nodes.Append(stdcomponents.BvhNode{-1}, stdcomponents.BvhNode{-1}) + t.AabbNodes.Append(stdcomponents.AABB{}, stdcomponents.AABB{}) + + // Set parent's childIndex to leftIndex + t.Nodes.Get(task.parentIndex).ChildIndex = int32(leftIndex) + + // Push parent task back with childrenCreated=true + stack = append(stack, buildTask{ + parentIndex: task.parentIndex, + start: task.start, + end: task.end, + childrenCreated: true, + }) + + // Push right child task (split+1 to end) + stack = append(stack, buildTask{ + parentIndex: leftIndex + 1, + start: split + 1, + end: task.end, + childrenCreated: false, + }) + + // Push left child task (start to split) + stack = append(stack, buildTask{ + parentIndex: leftIndex, + start: task.start, + end: split, + childrenCreated: false, + }) + } else { + // Merge children's AABBs into parent + leftChildIndex := int(t.Nodes.Get(task.parentIndex).ChildIndex) + rightChildIndex := leftChildIndex + 1 + + leftAABB := t.AabbNodes.Get(leftChildIndex) + rightAABB := t.AabbNodes.Get(rightChildIndex) + + merged := s.mergeAABB(leftAABB, rightAABB) + t.AabbNodes.Set(task.parentIndex, merged) + } + } + t.Components.Reset() +} + +func (s *BvhTreeSystem) findSplit(t *stdcomponents.BvhTree, start, end int) int { + // Identical Morton sortedMortonCodes => split the range in the middle. + first := t.Codes.GetValue(start) + last := t.Codes.GetValue(end) + + if first == last { + return (start + end) >> 1 + } + + // Calculate the number of highest bits that are the same + // for all objects, using the count-leading-zeros intrinsic. + commonPrefix := bits.LeadingZeros64(first ^ last) + + // Use binary search to find where the next bit differs. + // Specifically, we are looking for the highest object that + // shares more than commonPrefix bits with the first one. + split := start + step := end - start + + for { + step = (step + 1) >> 1 // exponential decrease + newSplit := split + step // proposed new position + + if newSplit < end { + splitCode := t.Codes.GetValue(newSplit) + splitPrefix := bits.LeadingZeros64(first ^ splitCode) + if splitPrefix > commonPrefix { + split = newSplit + } + } + + if step <= 1 { + break + } + } + + return split +} + +func (s *BvhTreeSystem) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { + return stdcomponents.AABB{ + Min: vectors.Vec2{ + X: min(a.Min.X, b.Min.X), + Y: min(a.Min.Y, b.Min.Y), + }, + Max: vectors.Vec2{ + X: max(a.Max.X, b.Max.X), + Y: max(a.Max.Y, b.Max.Y), + }, + } +} From d33f37ff59f424783ebb3e7cca8f648b8ef94d1f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 7 Apr 2025 19:21:26 +0300 Subject: [PATCH 112/196] upd collision-detection.go implement grid and tree setup sub system --- examples/new-api/entities/spaceship.go | 12 +- examples/new-api/game.go | 6 +- examples/new-api/instances/component-list.go | 4 + examples/new-api/instances/system-list.go | 2 + examples/new-api/systems/asterodd.go | 2 +- examples/new-api/systems/render-assterodd.go | 375 ------------------- stdcomponents/bvh-tree.go | 163 ++++++++ stdcomponents/collision-chunk.go | 30 ++ stdcomponents/collision-grid.go | 24 +- stdcomponents/ids.go | 1 + stdcomponents/spatial-index.go | 9 +- stdsystems/collision-detection-bvh.go | 107 ++---- stdsystems/collision-detection.go | 194 ++++++++++ stdsystems/render.go | 75 +++- 14 files changed, 539 insertions(+), 465 deletions(-) delete mode 100644 examples/new-api/systems/render-assterodd.go create mode 100644 stdcomponents/collision-chunk.go create mode 100644 stdsystems/collision-detection.go diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go index 04c89c85..99454383 100644 --- a/examples/new-api/entities/spaceship.go +++ b/examples/new-api/entities/spaceship.go @@ -43,6 +43,13 @@ type CreateSpaceShipManagers struct { SoundEffects *components.SoundEffectsComponentManager } +func createMask(layers ...stdcomponents.CollisionLayer) (mask stdcomponents.CollisionMask) { + for _, layer := range layers { + mask |= 1 << layer + } + return mask +} + func CreateSpaceShip( props CreateSpaceShipManagers, posX, posY float32, @@ -87,8 +94,9 @@ func CreateSpaceShip( X: 16, Y: 16, }, - Layer: config.PlayerCollisionLayer, - Mask: 1<= 8192 { - if len(s.instanceData) > 0 { - s.submitBatch(s.instanceData) - s.instanceData = s.instanceData[:0] - } - currentTex = entry.TextureId - } - s.instanceData = append(s.instanceData, s.getInstanceData(entry.Entity)) - } - s.submitBatch(s.instanceData) // Submit last batch - s.renderList = s.renderList[:0] - - // ========== - // DEBUG - // ========== - if s.debug { - rl.BeginMode2D(s.camera) - s.AABBs.EachEntity(func(e ecs.Entity) bool { - aabb := s.AABBs.Get(e) - clr := rl.Green - isSleeping := s.ColliderSleepStateComponentManager.Get(e) - if isSleeping != nil { - clr = rl.Blue - } - isTree := s.BvhTrees.Get(e) - if isTree != nil { - rl.DrawRectangle(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), isTree.Color) - return true - } - rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), clr) - return true - }) - s.Collisions.EachEntity(func(entity ecs.Entity) bool { - pos := s.Positions.Get(entity) - rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) - return true - }) - rl.EndMode2D() - } -} - -func (s *RenderAssteroddSystem) submitBatch(data []stdcomponents.RLTexturePro) { - rl.BeginMode2D(s.camera) - if s.debug { - for i := range data { - rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) - rl.DrawRectangle(int32(data[i].Dest.X-2), int32(data[i].Dest.Y-2), 4, 4, rl.Red) - } - } else { - for i := range data { - rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) - } - } - rl.EndMode2D() -} - -func (s *RenderAssteroddSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { - return *s.RlTexturePros.Get(e) -} - -func (s *RenderAssteroddSystem) prepareRender(dt time.Duration) { - wg := new(sync.WaitGroup) - wg.Add(6) - s.prepareAnimations(wg) - go s.prepareFlips(wg) - go s.preparePositions(wg, dt) - go s.prepareRotations(wg) - go s.prepareScales(wg) - go s.prepareTints(wg) - wg.Wait() -} - -func (s *RenderAssteroddSystem) prepareAnimations(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - animation := s.AnimationPlayers.Get(entity) - if animation == nil { - return true - } - frame := &texturePro.Frame - if animation.Vertical { - frame.Y += frame.Height * float32(animation.Current) - } else { - frame.X += frame.Width * float32(animation.Current) - } - return true - }) -} - -func (s *RenderAssteroddSystem) prepareFlips(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - flipped := s.Flips.Get(entity) - if flipped == nil { - return true - } - if flipped.X { - texturePro.Frame.Width *= -1 - } - if flipped.Y { - texturePro.Frame.Height *= -1 - } - return true - }) -} - -func (s *RenderAssteroddSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { - defer wg.Done() - dts := dt.Seconds() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - position := s.Positions.Get(entity) - if position == nil { - return true - } - decay := 40.0 // DECAY IS TICKRATE DEPENDENT - x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) - y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) - texturePro.Dest.X = x - texturePro.Dest.Y = y - player := s.Player.Get(entity) - if player != nil { - s.camera.Target.X = x - s.camera.Target.Y = y - } - - return true - }) -} - -func (s *RenderAssteroddSystem) prepareRotations(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - rotation := s.Rotations.Get(entity) - if rotation == nil { - return true - } - texturePro.Rotation = float32(rotation.Degrees()) - return true - }) -} - -func (s *RenderAssteroddSystem) prepareScales(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - scale := s.Scales.Get(entity) - if scale == nil { - return true - } - texturePro.Dest.Width *= scale.XY.X - texturePro.Dest.Height *= scale.XY.Y - return true - }) -} - -func (s *RenderAssteroddSystem) prepareTints(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - tr := s.RlTexturePros.Get(entity) - tint := s.Tints.Get(entity) - if tint == nil { - return true - } - trTint := &tr.Tint - trTint.A = tint.A - trTint.R = tint.R - trTint.G = tint.G - trTint.B = tint.B - return true - }) -} - -func (s *RenderAssteroddSystem) expDecay(a, b, decay, dt float64) float64 { - return b + (a-b)*(math.Exp(-decay*dt)) -} diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index 60ceafa9..02bd7dfd 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -17,7 +17,10 @@ package stdcomponents import ( "github.com/negrel/assert" "gomp/pkg/ecs" + "gomp/vectors" "math" + "math/bits" + "slices" ) const mortonPrecision = (1 << 16) - 1 @@ -141,6 +144,166 @@ func (t *BvhTree) morton2D(aabb *AABB) uint64 { return xx | (yy << 1) } +func (t *BvhTree) Build() { + // Reset tree + t.Nodes.Reset() + t.AabbNodes.Reset() + t.Leaves.Reset() + t.AabbLeaves.Reset() + t.Codes.Reset() + + // Extract and sort components by morton code + if cap(t.ComponentsSlice) < t.Components.Len() { + t.ComponentsSlice = make([]BvhComponent, 0, t.Components.Len()) + } + + t.ComponentsSlice = t.Components.Raw(t.ComponentsSlice) + + slices.SortFunc(t.ComponentsSlice, func(a, b BvhComponent) int { + return int(a.Code - b.Code) + }) + + // Add leaves + for i := range t.ComponentsSlice { + component := &t.ComponentsSlice[i] + t.Leaves.Append(BvhLeaf{Id: component.Entity}) + t.AabbLeaves.Append(component.Aabb) + t.Codes.Append(component.Code) + } + t.Components.Reset() + + if t.Leaves.Len() == 0 { + return + } + + // Add root node + t.Nodes.Append(BvhNode{-1}) + t.AabbNodes.Append(AABB{}) + + type buildTask struct { + parentIndex int + start int + end int + childrenCreated bool + } + + stack := []buildTask{ + {parentIndex: 0, start: 0, end: t.Leaves.Len() - 1, childrenCreated: false}, + } + + for len(stack) > 0 { + // Pop the last task + task := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + if !task.childrenCreated { + if task.start == task.end { + // Leaf node + t.Nodes.Get(task.parentIndex).ChildIndex = -int32(task.start) + t.AabbNodes.Set(task.parentIndex, t.AabbLeaves.GetValue(task.start)) + continue + } + + split := t.findSplit(task.start, task.end) + + // Create left and right nodes + leftIndex := t.Nodes.Len() + t.Nodes.Append(BvhNode{-1}, BvhNode{-1}) + t.AabbNodes.Append(AABB{}, AABB{}) + + // Set parent's childIndex to leftIndex + t.Nodes.Get(task.parentIndex).ChildIndex = int32(leftIndex) + + // Push parent task back with childrenCreated=true + stack = append(stack, buildTask{ + parentIndex: task.parentIndex, + start: task.start, + end: task.end, + childrenCreated: true, + }) + + // Push right child task (split+1 to end) + stack = append(stack, buildTask{ + parentIndex: leftIndex + 1, + start: split + 1, + end: task.end, + childrenCreated: false, + }) + + // Push left child task (start to split) + stack = append(stack, buildTask{ + parentIndex: leftIndex, + start: task.start, + end: split, + childrenCreated: false, + }) + } else { + // Merge children's AABBs into parent + leftChildIndex := int(t.Nodes.Get(task.parentIndex).ChildIndex) + rightChildIndex := leftChildIndex + 1 + + leftAABB := t.AabbNodes.Get(leftChildIndex) + rightAABB := t.AabbNodes.Get(rightChildIndex) + + merged := t.mergeAABB(leftAABB, rightAABB) + t.AabbNodes.Set(task.parentIndex, merged) + } + } + t.Components.Reset() +} + +func (t *BvhTree) findSplit(start, end int) int { + // Identical Morton sortedMortonCodes => split the range in the middle. + first := t.Codes.GetValue(start) + last := t.Codes.GetValue(end) + + if first == last { + return (start + end) >> 1 + } + + // Calculate the number of highest bits that are the same + // for all objects, using the count-leading-zeros intrinsic. + commonPrefix := bits.LeadingZeros64(first ^ last) + + // Use binary search to find where the next bit differs. + // Specifically, we are looking for the highest object that + // shares more than commonPrefix bits with the first one. + split := start + step := end - start + + for { + step = (step + 1) >> 1 // exponential decrease + newSplit := split + step // proposed new position + + if newSplit < end { + splitCode := t.Codes.GetValue(newSplit) + splitPrefix := bits.LeadingZeros64(first ^ splitCode) + if splitPrefix > commonPrefix { + split = newSplit + } + } + + if step <= 1 { + break + } + } + + return split +} + +func (t *BvhTree) mergeAABB(a, b *AABB) AABB { + return AABB{ + Min: vectors.Vec2{ + X: min(a.Min.X, b.Min.X), + Y: min(a.Min.Y, b.Min.Y), + }, + Max: vectors.Vec2{ + X: max(a.Max.X, b.Max.X), + Y: max(a.Max.Y, b.Max.Y), + }, + } +} + type BvhTreeComponentManager = ecs.ComponentManager[BvhTree] func NewBvhTreeComponentManager() BvhTreeComponentManager { diff --git a/stdcomponents/collision-chunk.go b/stdcomponents/collision-chunk.go new file mode 100644 index 00000000..6ae15b97 --- /dev/null +++ b/stdcomponents/collision-chunk.go @@ -0,0 +1,30 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" +) + +type CollisionChunk struct { + Size float32 + Layer CollisionLayer +} + +type CollisionChunkComponentManager = ecs.ComponentManager[CollisionChunk] + +func NewCollisionChunkComponentManager() CollisionChunkComponentManager { + return ecs.NewComponentManager[CollisionChunk](CollisionChunkComponentId) +} diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go index 6da5cf43..3a3c5c99 100644 --- a/stdcomponents/collision-grid.go +++ b/stdcomponents/collision-grid.go @@ -20,22 +20,28 @@ import ( ) type CollisionGrid struct { - Layer CollisionLayer // Layer of the grid - Entities ecs.PagedArray[ecs.Entity] // List of Entities in the grid - CellLookup map[SpatialIndex]ecs.Entity // Pointer to cell + Layer CollisionLayer // Layer of the grid + Entities ecs.PagedArray[ecs.Entity] // List of Entities in the grid + ChunkLookup map[SpatialIndex]ecs.Entity // Pointer to cell - MinBounds AABB // Bounds of the smallest entity in the grid - CellSize vectors.Vec2 + ChunkSize float32 + MinBounds vectors.Vec2 } func (t *CollisionGrid) RegisterEntity(entity ecs.Entity, aabb *AABB) { t.Entities.Append(entity) - if aabb.Min.X < t.MinBounds.Min.X { - t.MinBounds.Min.X = aabb.Min.X + l := aabb.Max.Sub(aabb.Min) + + if l.LengthSquared() < t.MinBounds.LengthSquared() { + t.MinBounds = l } - if aabb.Min.Y < t.MinBounds.Min.Y { - t.MinBounds.Min.Y = aabb.Min.Y +} + +func (t *CollisionGrid) GetSpatialIndex(position vectors.Vec2) SpatialIndex { + return SpatialIndex{ + X: int(position.X / t.ChunkSize), + Y: int(position.Y / t.ChunkSize), } } diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index c2e62791..7ab556ef 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -41,6 +41,7 @@ const ( RigidBodyComponentId BvhTreeComponentId CollisionGridComponentId + CollisionChunkComponentId StdComponentIds // StdComponentIds MUST always be the last ) diff --git a/stdcomponents/spatial-index.go b/stdcomponents/spatial-index.go index 745bb09e..a918a306 100644 --- a/stdcomponents/spatial-index.go +++ b/stdcomponents/spatial-index.go @@ -14,12 +14,19 @@ Thank you for your support! package stdcomponents -import "gomp/pkg/ecs" +import ( + "gomp/pkg/ecs" + "gomp/vectors" +) type SpatialIndex struct { X, Y int } +func (i SpatialIndex) ToVec2() vectors.Vec2 { + return vectors.Vec2{X: float32(i.X), Y: float32(i.Y)} +} + type SpatialIndexComponentManager = ecs.ComponentManager[SpatialIndex] func NewSpatialIndexComponentManager() SpatialIndexComponentManager { diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index c720ffe1..28eddbe6 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -20,7 +20,6 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" - "image/color" "runtime" "sync" "time" @@ -100,51 +99,56 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { wg.Add(len(s.trees)) for i := range s.trees { go func(i int, w *sync.WaitGroup) { + defer w.Done() s.trees[i].Build() - w.Done() }(i, wg) } wg.Wait() - if debugTree { - s.BvhTreeComponentManager.EachEntity(func(entity ecs.Entity) bool { - s.EntityManager.Delete(entity) - return true - }) - - for i := range s.trees { - tree := s.trees[i] - treeColor := color.RGBA{ - R: uint8(i * 255 / len(s.trees)), - G: uint8((i + 1) * 255 / len(s.trees)), - B: uint8((i + 2) * 255 / len(s.trees)), - A: 30, - } - tree.AabbNodes.AllData(func(aabb *stdcomponents.AABB) bool { - e := s.EntityManager.Create() - s.BvhTreeComponentManager.Create(e, stdcomponents.BvhTree{ - Color: treeColor, - }) - s.AABB.Create(e, *aabb) - return true - }) - } - } - if len(s.entities) < s.GenericCollider.Len() { s.entities = make([]ecs.Entity, 0, s.GenericCollider.Len()) } s.entities = s.GenericCollider.RawEntities(s.entities) s.findEntityCollisions(s.entities) + s.registerCollisionEvents() +} + +func (s *CollisionDetectionBVHSystem) Destroy() {} + +func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity) { + var wg sync.WaitGroup + entitiesLength := len(entities) + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(entitiesLength/128, maxNumWorkers), 1) + chunkSize := entitiesLength / numWorkers - // could be used, but needs a worker id info - //s.AABB.EachEntityParallel(func(entity ecs.Entity) bool { - // - // potentialEntities := s.broadPhase(entity, make([]ecs.Entity, 0, 64)) - // s.narrowPhase(entity, potentialEntities, id) - // return true - //}) + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = entitiesLength + } + + go func(start int, end int, id int) { + defer wg.Done() + + for i := range entities[start:end] { + entityA := entities[i+startIndex] + + potentialEntities := s.broadPhase(entityA, make([]ecs.Entity, 0, 64)) + if len(potentialEntities) == 0 { + continue + } + s.narrowPhase(entityA, potentialEntities, id) + } + }(startIndex, endIndex, workedId) + } + // Wait for workers and close collision channel + wg.Wait() +} +func (s *CollisionDetectionBVHSystem) registerCollisionEvents() { for i := range s.collisionEvents { events := &s.collisionEvents[i] events.AllData(func(event *CollisionEvent) bool { @@ -184,41 +188,6 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } } -func (s *CollisionDetectionBVHSystem) Destroy() {} - -func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity) { - var wg sync.WaitGroup - entitiesLength := len(entities) - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(entitiesLength/128, maxNumWorkers), 1) - chunkSize := entitiesLength / numWorkers - - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = entitiesLength - } - - go func(start int, end int, id int) { - defer wg.Done() - - for i := range entities[start:end] { - entityA := entities[i+startIndex] - - potentialEntities := s.broadPhase(entityA, make([]ecs.Entity, 0, 64)) - if len(potentialEntities) == 0 { - continue - } - s.narrowPhase(entityA, potentialEntities, id) - } - }(startIndex, endIndex, workedId) - } - // Wait for workers and close collision channel - wg.Wait() -} - func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, result []ecs.Entity) []ecs.Entity { colliderA := s.GenericCollider.Get(entityA) if colliderA.AllowSleep { diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go new file mode 100644 index 00000000..f1106c74 --- /dev/null +++ b/stdsystems/collision-detection.go @@ -0,0 +1,194 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "github.com/negrel/assert" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "image/color" + "math" + "math/rand" + "sync" + "time" +) + +const ( + chunkScaleFactor = 32 +) + +func NewCollisionDetectionSystem() CollisionDetectionSystem { + return CollisionDetectionSystem{} +} + +type CollisionDetectionSystem struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Tints *stdcomponents.TintComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + PolygonColliders *stdcomponents.PolygonColliderComponentManager + Collisions *stdcomponents.CollisionComponentManager + SpatialIndex *stdcomponents.SpatialIndexComponentManager + AABB *stdcomponents.AABBComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + BvhTreeComponentManager *stdcomponents.BvhTreeComponentManager + CollisionGridComponentManager *stdcomponents.CollisionGridComponentManager + CollisionChunkComponentManager *stdcomponents.CollisionChunkComponentManager + + gridLookup map[stdcomponents.CollisionLayer]ecs.Entity +} + +func (s *CollisionDetectionSystem) Init() { + s.gridLookup = make(map[stdcomponents.CollisionLayer]ecs.Entity) +} +func (s *CollisionDetectionSystem) Run(dt time.Duration) { + s.setup() + +} +func (s *CollisionDetectionSystem) Destroy() {} +func (s *CollisionDetectionSystem) setup() { + // Reset grids + s.CollisionGridComponentManager.EachEntity(func(entity ecs.Entity) bool { + grid := s.CollisionGridComponentManager.Get(entity) + assert.NotNil(grid) + + grid.Entities.Reset() + grid.MinBounds = vectors.Vec2{ + X: math.MaxFloat32, + Y: math.MaxFloat32, + } + + return true + }) + + // Register all entities in grids + s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + collider := s.GenericCollider.Get(entity) + assert.NotNil(collider) + + gridEntity, exists := s.gridLookup[collider.Layer] + if !exists { + gridEntity = s.EntityManager.Create() + s.gridLookup[collider.Layer] = gridEntity + s.CollisionGridComponentManager.Create(gridEntity, stdcomponents.CollisionGrid{ + Layer: collider.Layer, + Entities: ecs.NewPagedArray[ecs.Entity](), + ChunkLookup: make(map[stdcomponents.SpatialIndex]ecs.Entity), + ChunkSize: 0, + MinBounds: vectors.Vec2{ + X: math.MaxFloat32, + Y: math.MaxFloat32, + }, + }) + } + + grid := s.CollisionGridComponentManager.Get(gridEntity) + assert.NotNil(grid) + + position := s.Positions.Get(entity) + assert.NotNil(position) + + aabb := s.AABB.Get(entity) + assert.NotNil(aabb) + + grid.RegisterEntity(entity, aabb) + + return true + }) + + // Distribute entities in grids + s.CollisionGridComponentManager.EachEntity(func(gridEntity ecs.Entity) bool { + grid := s.CollisionGridComponentManager.Get(gridEntity) + assert.NotNil(grid) + + newChunkSize := grid.MinBounds.Length() * chunkScaleFactor + newChunkSize = float32(min(int(1)<<(ecs.FastIntLog2(int(newChunkSize))+1), 65535)) + if newChunkSize != grid.ChunkSize { + for i, c := range grid.ChunkLookup { + s.EntityManager.Delete(c) + delete(grid.ChunkLookup, i) + } + + grid.ChunkSize = newChunkSize + } + + grid.Entities.AllDataValue(func(entity ecs.Entity) bool { + position := s.Positions.Get(entity) + assert.NotNil(position) + + spatialIndex := grid.GetSpatialIndex(position.XY) + chunkEntity, exists := grid.ChunkLookup[spatialIndex] + if !exists { + chunkEntity = s.EntityManager.Create() + grid.ChunkLookup[spatialIndex] = chunkEntity + s.CollisionChunkComponentManager.Create(chunkEntity, stdcomponents.CollisionChunk{ + Size: grid.ChunkSize, + Layer: grid.Layer, + }) + chunkPos := s.Positions.Create(chunkEntity, stdcomponents.Position{ + XY: spatialIndex.ToVec2().Scale(grid.ChunkSize), + }) + assert.NotNil(chunkPos) + s.BvhTreeComponentManager.Create(chunkEntity, stdcomponents.BvhTree{ + Nodes: ecs.NewPagedArray[stdcomponents.BvhNode](), + AabbNodes: ecs.NewPagedArray[stdcomponents.AABB](), + Leaves: ecs.NewPagedArray[stdcomponents.BvhLeaf](), + AabbLeaves: ecs.NewPagedArray[stdcomponents.AABB](), + Codes: ecs.NewPagedArray[uint64](), + Components: ecs.NewPagedArray[stdcomponents.BvhComponent](), + ComponentsSlice: make([]stdcomponents.BvhComponent, 0, grid.Entities.Len()), + }) + s.Tints.Create(chunkEntity, color.RGBA{ + R: uint8(rand.Intn(255)), + G: uint8(rand.Intn(255)), + B: uint8(rand.Intn(255)), + A: 20, + }) + } + + tree := s.BvhTreeComponentManager.Get(chunkEntity) + assert.NotNil(tree) + + aabb := s.AABB.Get(entity) + assert.NotNil(aabb) + + tree.AddComponent(entity, *aabb) + + return true + }) + return true + }) + + // Build BVH trees + wg := &sync.WaitGroup{} + s.CollisionChunkComponentManager.EachEntity(func(gridEntity ecs.Entity) bool { + tree := s.BvhTreeComponentManager.Get(gridEntity) + assert.NotNil(tree) + + wg.Add(1) + go func() { + defer wg.Done() + tree.Build() + }() + + return true + }) + wg.Wait() +} diff --git a/stdsystems/render.go b/stdsystems/render.go index ffab1536..9560144a 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -17,8 +17,10 @@ package stdsystems import ( "fmt" rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" + "image/color" "math" "slices" "sync" @@ -51,6 +53,7 @@ type RenderSystem struct { Collisions *stdcomponents.CollisionComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager BvhTrees *stdcomponents.BvhTreeComponentManager + CollisionChunks *stdcomponents.CollisionChunkComponentManager renderList []renderEntry instanceData []stdcomponents.RLTexturePro @@ -59,7 +62,8 @@ type RenderSystem struct { monitorWidth int monitorHeight int - debug bool + debug bool + debugLvl int } type renderEntry struct { @@ -69,7 +73,7 @@ type renderEntry struct { } func (s *RenderSystem) Init() { - rl.InitWindow(1280, 720, "GOMP") + rl.InitWindow(0, 0, "GOMP") s.monitorWidth = rl.GetScreenWidth() s.monitorHeight = rl.GetScreenHeight() s.camera = rl.Camera2D{ @@ -88,6 +92,33 @@ func (s *RenderSystem) Run(dt time.Duration) bool { if rl.IsKeyPressed(rl.KeyF12) { s.debug = !s.debug } + if rl.IsKeyPressed(rl.KeyF11) { + s.debugLvl++ + if s.debugLvl > 63 { + s.debugLvl = 0 + } + } + if rl.IsKeyPressed(rl.KeyF10) { + s.debugLvl-- + if s.debugLvl < 0 { + s.debugLvl = 63 + } + } + + fdt := float32(dt.Seconds()) + + if rl.IsKeyDown(rl.KeyLeft) { + s.camera.Target.X -= 1000 * fdt + } + if rl.IsKeyDown(rl.KeyRight) { + s.camera.Target.X += 1000 * fdt + } + if rl.IsKeyDown(rl.KeyUp) { + s.camera.Target.Y -= 1000 * fdt + } + if rl.IsKeyDown(rl.KeyDown) { + s.camera.Target.Y += 1000 * fdt + } s.prepareRender(dt) @@ -97,6 +128,7 @@ func (s *RenderSystem) Run(dt time.Duration) bool { rl.DrawFPS(10, 10) rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) + rl.DrawText(fmt.Sprintf("%d debugLvl", s.debugLvl), 10, 50, 20, rl.RayWhite) rl.EndDrawing() return false @@ -126,6 +158,7 @@ type RenderInjector struct { Collisions *stdcomponents.CollisionComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager BvhTrees *stdcomponents.BvhTreeComponentManager + CollisionChunks *stdcomponents.CollisionChunkComponentManager } func (s *RenderSystem) InjectWorld(injector *RenderInjector) { @@ -148,6 +181,7 @@ func (s *RenderSystem) InjectWorld(injector *RenderInjector) { s.Collisions = injector.Collisions s.ColliderSleepStateComponentManager = injector.ColliderSleepStateComponentManager s.BvhTrees = injector.BvhTrees + s.CollisionChunks = injector.CollisionChunks } func (s *RenderSystem) render() { @@ -156,6 +190,38 @@ func (s *RenderSystem) render() { // ========== if s.debug { rl.BeginMode2D(s.camera) + s.CollisionChunks.EachEntity(func(e ecs.Entity) bool { + chunk := s.CollisionChunks.Get(e) + assert.NotNil(chunk) + + if chunk.Layer != stdcomponents.CollisionLayer(s.debugLvl) { + return true + } + + tint := s.Tints.Get(e) + assert.NotNil(tint) + + position := s.Positions.Get(e) + assert.NotNil(position) + + tree := s.BvhTrees.Get(e) + assert.NotNil(tree) + + tree.AabbNodes.AllData(func(a *stdcomponents.AABB) bool { + rl.DrawRectangle(int32(a.Min.X), int32(a.Min.Y), int32(a.Max.X-a.Min.X), int32(a.Max.Y-a.Min.Y), *tint) + return true + }) + + clr := color.RGBA{ + R: tint.R, + G: tint.G, + B: tint.B, + A: 255, + } + + rl.DrawRectangleLines(int32(position.XY.X), int32(position.XY.Y), int32(chunk.Size), int32(chunk.Size), clr) + return true + }) s.BoxColliders.EachEntity(func(e ecs.Entity) bool { col := s.BoxColliders.Get(e) scale := s.Scales.Get(e) @@ -256,11 +322,6 @@ func (s *RenderSystem) render() { if isSleeping != nil { clr = rl.Blue } - isTree := s.BvhTrees.Get(e) - if isTree != nil { - rl.DrawRectangle(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), isTree.Color) - return true - } rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), clr) return true }) From 22683681ecba364519f6261584334a04b31abb2c Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 9 Apr 2025 11:54:16 +0300 Subject: [PATCH 113/196] init slice.go --- pkg/ecs/slice.go | 317 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 pkg/ecs/slice.go diff --git a/pkg/ecs/slice.go b/pkg/ecs/slice.go new file mode 100644 index 00000000..bb644415 --- /dev/null +++ b/pkg/ecs/slice.go @@ -0,0 +1,317 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package ecs + +import ( + "runtime" + "sync" + + "github.com/negrel/assert" +) + +type Slice[T any] struct { + data []T + len int + parallelCount uint8 +} + +func NewSlice[T any](size int) (a Slice[T]) { + a.data = make([]T, 0, size) + a.parallelCount = uint8(runtime.NumCPU()) - 2 + + return a +} + +func (a *Slice[T]) Len() int { + return a.len +} + +func (a *Slice[T]) Get(index int) *T { + assert.True(index >= 0, "index out of range") + assert.True(index < a.len, "index out of range") + + return &a.data[index] +} + +func (a *Slice[T]) GetValue(index int) T { + assert.True(index >= 0, "index out of range") + assert.True(index < a.len, "index out of range") + + return a.data[index] +} + +func (a *Slice[T]) Set(index int, value T) *T { + assert.True(index >= 0, "index out of range") + assert.True(index < a.len, "index out of range") + + a.data[index] = value + + return &a.data[index] +} + +func (a *Slice[T]) Append(values ...T) *T { + var result *T + for i := range values { + value := values[i] + if a.currentPageIndex >= len(a.data) { + newBooks := make([]ArrayPage[T], len(a.data)*2) + a.data = append(a.data, newBooks...) + } + + page := &a.data[a.currentPageIndex] + + if page.len == pageSize { + a.currentPageIndex++ + if a.currentPageIndex >= len(a.data) { + newBooks := make([]ArrayPage[T], len(a.data)*2) + a.data = append(a.data, newBooks...) + } + page = &a.data[a.currentPageIndex] + } + page.data[page.len] = value + result = &page.data[page.len] + page.len++ + a.len++ + } + + return result +} + +func (a *Slice[T]) SoftReduce() { + assert.True(a.len > 0, "Len is already 0") + a.len-- +} + +func (a *Slice[T]) Reset() { + a.len = 0 +} + +func (a *Slice[T]) Copy(fromIndex, toIndex int) { + assert.True(fromIndex >= 0, "index out of range") + assert.True(fromIndex < a.len, "index out of range") + from := a.Get(fromIndex) + + assert.True(toIndex >= 0, "index out of range") + assert.True(toIndex < a.len, "index out of range") + to := a.Get(toIndex) + + *to = *from +} + +func (a *Slice[T]) Swap(i, j int) (newI, NewJ *T) { + assert.True(i >= 0, "index out of range") + assert.True(i < a.len, "index out of range") + x := a.Get(i) + + assert.True(j >= 0, "index out of range") + assert.True(j < a.len, "index out of range") + y := a.Get(j) + + *x, *y = *y, *x + return x, y +} + +func (a *Slice[T]) Last() *T { + index := a.len - 1 + assert.True(index >= 0, "index out of range") + + return a.Get(index) +} + +func (a *Slice[T]) Raw(result []T) []T { + result = result[:0] + for i := 0; i <= a.currentPageIndex; i++ { + page := &a.data[i] + result = append(result[:i*1024], append(result[i*1024:], page.data[:page.len]...)...) + } + + return result +} + +func (a *Slice[T]) getPageIdAndIndex(index int) (int, int) { + pageId := index >> pageSizeShift + assert.True(pageId < len(a.data), "index out of range") + + index %= pageSize + assert.True(index < pageSize, "index out of range") + + return pageId, index +} + +func (a *Slice[T]) All(yield func(int, *T) bool) { + var page *ArrayPage[T] + var index_offset int + + book := a.data + + if a.len == 0 { + return + } + + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] + index_offset = i << pageSizeShift + + for j := page.len - 1; j >= 0; j-- { + if !yield(index_offset+j, &page.data[j]) { + return + } + } + } +} + +func (a *Slice[T]) AllParallel(yield func(int, *T) bool) { + var page *ArrayPage[T] + var data *[pageSize]T + var index_offset int + + book := a.data + wg := new(sync.WaitGroup) + gorutineBudget := a.parallelCount + + runner := func(data *[pageSize]T, offset int, startIndex int, wg *sync.WaitGroup) { + defer wg.Done() + for j := startIndex; j >= 0; j-- { + if !yield(offset+j, &(data[j])) { + return + } + } + } + + if a.len == 0 { + return + } + + wg.Add(int(a.currentPageIndex) + 1) + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] + data = &page.data + index_offset = int(i) << pageSizeShift + + if gorutineBudget > 0 { + go runner(data, index_offset, page.len-1, wg) + gorutineBudget-- + continue + } + + runner(data, index_offset, page.len-1, wg) + } + + wg.Wait() +} + +func (a *Slice[T]) AllData(yield func(*T) bool) { + var page *ArrayPage[T] + + book := a.data + + if a.len == 0 { + return + } + + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] + + for j := page.len - 1; j >= 0; j-- { + if !yield(&page.data[j]) { + return + } + } + } +} + +func (a *Slice[T]) AllDataValue(yield func(T) bool) { + var page *ArrayPage[T] + + book := a.data + + if a.len == 0 { + return + } + + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] + + for j := page.len - 1; j >= 0; j-- { + if !yield(page.data[j]) { + return + } + } + } +} + +func (a *Slice[T]) AllDataValueParallel(yield func(T) bool) { + var page *ArrayPage[T] + var data *[pageSize]T + + book := a.data + wg := new(sync.WaitGroup) + gorutineBudget := a.parallelCount + runner := func(data *[pageSize]T, startIndex int, wg *sync.WaitGroup) { + defer wg.Done() + for j := startIndex; j >= 0; j-- { + if !yield((data[j])) { + return + } + } + } + + if a.len == 0 { + return + } + + wg.Add(int(a.currentPageIndex) + 1) + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] + data = &page.data + + if gorutineBudget > 0 { + go runner(data, page.len-1, wg) + gorutineBudget-- + continue + } + + runner(data, page.len-1, wg) + } + wg.Wait() +} + +func (a *Slice[T]) AllDataParallel(yield func(*T) bool) { + var page *ArrayPage[T] + var data *[pageSize]T + + book := a.data + wg := new(sync.WaitGroup) + gorutineBudget := a.parallelCount + runner := func(data *[pageSize]T, startIndex int, wg *sync.WaitGroup) { + defer wg.Done() + for j := startIndex; j >= 0; j-- { + if !yield(&(data[j])) { + return + } + } + } + + if a.len == 0 { + return + } + + wg.Add(int(a.currentPageIndex) + 1) + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] + data = &page.data + + if gorutineBudget > 0 { + go runner(data, page.len-1, wg) + gorutineBudget-- + continue + } + + runner(data, page.len-1, wg) + } + wg.Wait() +} From 3632e01e2b9a528745b37b461d3ade108b181c57 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 9 Apr 2025 18:03:55 +0300 Subject: [PATCH 114/196] upd ecs slice --- pkg/ecs/slice.go | 104 +++++++++-------------------------------------- 1 file changed, 20 insertions(+), 84 deletions(-) diff --git a/pkg/ecs/slice.go b/pkg/ecs/slice.go index bb644415..660aa3e6 100644 --- a/pkg/ecs/slice.go +++ b/pkg/ecs/slice.go @@ -54,31 +54,9 @@ func (a *Slice[T]) Set(index int, value T) *T { } func (a *Slice[T]) Append(values ...T) *T { - var result *T - for i := range values { - value := values[i] - if a.currentPageIndex >= len(a.data) { - newBooks := make([]ArrayPage[T], len(a.data)*2) - a.data = append(a.data, newBooks...) - } - - page := &a.data[a.currentPageIndex] - - if page.len == pageSize { - a.currentPageIndex++ - if a.currentPageIndex >= len(a.data) { - newBooks := make([]ArrayPage[T], len(a.data)*2) - a.data = append(a.data, newBooks...) - } - page = &a.data[a.currentPageIndex] - } - page.data[page.len] = value - result = &page.data[page.len] - page.len++ - a.len++ - } - - return result + a.data = append(a.data[:a.len-1], values...) + a.len += len(values) + return &a.data[a.len-1] } func (a *Slice[T]) SoftReduce() { @@ -124,11 +102,7 @@ func (a *Slice[T]) Last() *T { func (a *Slice[T]) Raw(result []T) []T { result = result[:0] - for i := 0; i <= a.currentPageIndex; i++ { - page := &a.data[i] - result = append(result[:i*1024], append(result[i*1024:], page.data[:page.len]...)...) - } - + copy(result, a.data) return result } @@ -143,23 +117,25 @@ func (a *Slice[T]) getPageIdAndIndex(index int) (int, int) { } func (a *Slice[T]) All(yield func(int, *T) bool) { - var page *ArrayPage[T] - var index_offset int - - book := a.data - - if a.len == 0 { - return + for j := a.len - 1; j >= 0; j-- { + if !yield(j, &a.data[j]) { + return + } } +} - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - index_offset = i << pageSizeShift +func (a *Slice[T]) AllData(yield func(*T) bool) { + for j := a.len - 1; j >= 0; j-- { + if !yield(&a.data[j]) { + return + } + } +} - for j := page.len - 1; j >= 0; j-- { - if !yield(index_offset+j, &page.data[j]) { - return - } +func (a *Slice[T]) AllDataValue(yield func(T) bool) { + for j := a.len - 1; j >= 0; j-- { + if !yield(a.data[j]) { + return } } } @@ -204,46 +180,6 @@ func (a *Slice[T]) AllParallel(yield func(int, *T) bool) { wg.Wait() } -func (a *Slice[T]) AllData(yield func(*T) bool) { - var page *ArrayPage[T] - - book := a.data - - if a.len == 0 { - return - } - - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - - for j := page.len - 1; j >= 0; j-- { - if !yield(&page.data[j]) { - return - } - } - } -} - -func (a *Slice[T]) AllDataValue(yield func(T) bool) { - var page *ArrayPage[T] - - book := a.data - - if a.len == 0 { - return - } - - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - - for j := page.len - 1; j >= 0; j-- { - if !yield(page.data[j]) { - return - } - } - } -} - func (a *Slice[T]) AllDataValueParallel(yield func(T) bool) { var page *ArrayPage[T] var data *[pageSize]T From 1a528494b58854c0f1362fcfd7be3c97fc11ac83 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 9 Apr 2025 22:15:27 +0300 Subject: [PATCH 115/196] feat slice.go --- pkg/ecs/slice.go | 165 ++++++++++++++++++----------------------------- 1 file changed, 63 insertions(+), 102 deletions(-) diff --git a/pkg/ecs/slice.go b/pkg/ecs/slice.go index 660aa3e6..7f8aceda 100644 --- a/pkg/ecs/slice.go +++ b/pkg/ecs/slice.go @@ -16,12 +16,13 @@ import ( type Slice[T any] struct { data []T len int - parallelCount uint8 + wg sync.WaitGroup + maxNumWorkers int } func NewSlice[T any](size int) (a Slice[T]) { a.data = make([]T, 0, size) - a.parallelCount = uint8(runtime.NumCPU()) - 2 + a.maxNumWorkers = max(runtime.NumCPU()-2, 1) // Recover if less than 2 cpu return a } @@ -116,7 +117,7 @@ func (a *Slice[T]) getPageIdAndIndex(index int) (int, int) { return pageId, index } -func (a *Slice[T]) All(yield func(int, *T) bool) { +func (a *Slice[T]) Each(yield func(int, *T) bool) { for j := a.len - 1; j >= 0; j-- { if !yield(j, &a.data[j]) { return @@ -124,7 +125,7 @@ func (a *Slice[T]) All(yield func(int, *T) bool) { } } -func (a *Slice[T]) AllData(yield func(*T) bool) { +func (a *Slice[T]) EachData(yield func(*T) bool) { for j := a.len - 1; j >= 0; j-- { if !yield(&a.data[j]) { return @@ -132,7 +133,7 @@ func (a *Slice[T]) AllData(yield func(*T) bool) { } } -func (a *Slice[T]) AllDataValue(yield func(T) bool) { +func (a *Slice[T]) EachDataValue(yield func(T) bool) { for j := a.len - 1; j >= 0; j-- { if !yield(a.data[j]) { return @@ -140,114 +141,74 @@ func (a *Slice[T]) AllDataValue(yield func(T) bool) { } } -func (a *Slice[T]) AllParallel(yield func(int, *T) bool) { - var page *ArrayPage[T] - var data *[pageSize]T - var index_offset int +func (a *Slice[T]) EachParallel(batchSize int, yield func(int, *T, int) bool) { + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) + chunkSize := a.len / numWorkers - book := a.data - wg := new(sync.WaitGroup) - gorutineBudget := a.parallelCount - - runner := func(data *[pageSize]T, offset int, startIndex int, wg *sync.WaitGroup) { - defer wg.Done() - for j := startIndex; j >= 0; j-- { - if !yield(offset+j, &(data[j])) { - return - } - } - } - - if a.len == 0 { - return - } - - wg.Add(int(a.currentPageIndex) + 1) - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - data = &page.data - index_offset = int(i) << pageSizeShift - - if gorutineBudget > 0 { - go runner(data, index_offset, page.len-1, wg) - gorutineBudget-- - continue + a.wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } - - runner(data, index_offset, page.len-1, wg) - } - - wg.Wait() -} - -func (a *Slice[T]) AllDataValueParallel(yield func(T) bool) { - var page *ArrayPage[T] - var data *[pageSize]T - - book := a.data - wg := new(sync.WaitGroup) - gorutineBudget := a.parallelCount - runner := func(data *[pageSize]T, startIndex int, wg *sync.WaitGroup) { - defer wg.Done() - for j := startIndex; j >= 0; j-- { - if !yield((data[j])) { - return + go func(start int, end int) { + defer a.wg.Done() + for i := range a.data[start:end] { + if !yield(i, &a.data[i+startIndex], workedId) { + return + } } - } + }(startIndex, endIndex) } - - if a.len == 0 { - return - } - - wg.Add(int(a.currentPageIndex) + 1) - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - data = &page.data - - if gorutineBudget > 0 { - go runner(data, page.len-1, wg) - gorutineBudget-- - continue - } - - runner(data, page.len-1, wg) - } - wg.Wait() + a.wg.Wait() } -func (a *Slice[T]) AllDataParallel(yield func(*T) bool) { - var page *ArrayPage[T] - var data *[pageSize]T +func (a *Slice[T]) EachDataValueParallel(batchSize int, yield func(T, int) bool) { + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) + chunkSize := a.len / numWorkers - book := a.data - wg := new(sync.WaitGroup) - gorutineBudget := a.parallelCount - runner := func(data *[pageSize]T, startIndex int, wg *sync.WaitGroup) { - defer wg.Done() - for j := startIndex; j >= 0; j-- { - if !yield(&(data[j])) { - return - } + a.wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } + go func(start int, end int) { + defer a.wg.Done() + for i := range a.data[start:end] { + if !yield(a.data[i+startIndex], workedId) { + return + } + } + }(startIndex, endIndex) } + a.wg.Wait() +} - if a.len == 0 { - return - } - - wg.Add(int(a.currentPageIndex) + 1) - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - data = &page.data +func (a *Slice[T]) EachDataParallel(batchSize int, yield func(*T, int) bool) { + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) + chunkSize := a.len / numWorkers - if gorutineBudget > 0 { - go runner(data, page.len-1, wg) - gorutineBudget-- - continue + a.wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } - - runner(data, page.len-1, wg) + go func(start int, end int) { + defer a.wg.Done() + for i := range a.data[start:end] { + if !yield(&a.data[i+startIndex], workedId) { + return + } + } + }(startIndex, endIndex) } - wg.Wait() + a.wg.Wait() } From 68371642a1429109c0d6e50cebb4524bb7bf182c Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 10 Apr 2025 17:18:48 +0300 Subject: [PATCH 116/196] upd collision-detection.go --- examples/new-api/systems/render-bogdan.go | 12 +-- pkg/ecs/component-manager-shared.go | 8 +- pkg/ecs/component-manager.go | 8 +- pkg/ecs/paged-array.go | 104 +++++++++------------- pkg/ecs/slice.go | 10 +-- stdcomponents/bvh-tree.go | 51 +++++------ stdsystems/animation-player.go | 2 +- stdsystems/animation-spritematrix.go | 2 +- stdsystems/bvh-tree.go | 13 +-- stdsystems/collision-detection.go | 74 ++++++++------- stdsystems/render.go | 14 +-- 11 files changed, 139 insertions(+), 159 deletions(-) diff --git a/examples/new-api/systems/render-bogdan.go b/examples/new-api/systems/render-bogdan.go index dc605244..90d843ea 100644 --- a/examples/new-api/systems/render-bogdan.go +++ b/examples/new-api/systems/render-bogdan.go @@ -162,7 +162,7 @@ func (s *RenderBogdanSystem) prepareRender(dt time.Duration) { func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) animation := s.AnimationPlayers.Get(entity) if animation == nil { @@ -180,7 +180,7 @@ func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) mirrored := s.Flips.Get(entity) if mirrored == nil { @@ -199,7 +199,7 @@ func (s *RenderBogdanSystem) prepareFlips(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() //dts := dt.Seconds() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) position := s.Positions.Get(entity) if position == nil { @@ -217,7 +217,7 @@ func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Durati func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) rotation := s.Rotations.Get(entity) if rotation == nil { @@ -230,7 +230,7 @@ func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) scale := s.Scales.Get(entity) if scale == nil { @@ -244,7 +244,7 @@ func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareTints(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { tr := s.RlTexturePros.Get(entity) tint := s.Tints.Get(entity) if tint == nil { diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index c5666b54..336c715e 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -242,16 +242,16 @@ func (c *SharedComponentManager[T]) Each(yield func(Entity, *T) bool) { // Iterators Parallel // ======================================================== -func (c *SharedComponentManager[T]) EachComponentParallel(yield func(*T) bool) { +func (c *SharedComponentManager[T]) EachComponentParallel(batchSize int, yield func(*T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllDataParallel(yield) + c.components.AllDataParallel(batchSize, yield) } -func (c *SharedComponentManager[T]) EachEntityParallel(yield func(Entity) bool) { +func (c *SharedComponentManager[T]) EachEntityParallel(batchSize int, yield func(Entity, int) bool) { c.assertBegin() defer c.assertEnd() - c.entities.AllDataValueParallel(yield) + c.entities.AllDataValueParallel(batchSize, yield) } func (c *SharedComponentManager[T]) EachParallel(yield func(Entity, *T) bool) { diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 9e9026d3..f51bd246 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -239,16 +239,16 @@ func (c *ComponentManager[T]) Each(yield func(Entity, *T) bool) { // Iterators Parallel // ======================================================== -func (c *ComponentManager[T]) EachComponentParallel(yield func(*T) bool) { +func (c *ComponentManager[T]) EachComponentParallel(batchSize int, yield func(*T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllDataParallel(yield) + c.components.AllDataParallel(batchSize, yield) } -func (c *ComponentManager[T]) EachEntityParallel(yield func(Entity) bool) { +func (c *ComponentManager[T]) EachEntityParallel(batchSize int, yield func(Entity, int) bool) { c.assertBegin() defer c.assertEnd() - c.entities.AllDataValueParallel(yield) + c.entities.AllDataValueParallel(batchSize, yield) } func (c *ComponentManager[T]) EachParallel(yield func(Entity, *T) bool) { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index c82de76a..d8cd1381 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -17,12 +17,12 @@ type PagedArray[T any] struct { book []ArrayPage[T] currentPageIndex int len int - parallelCount uint8 + maxNumWorkers int } func NewPagedArray[T any]() (a PagedArray[T]) { a.book = make([]ArrayPage[T], 2, initialBookSize) - a.parallelCount = uint8(runtime.NumCPU()) - 2 + a.maxNumWorkers = max(runtime.NumCPU()-2, 1) // Recover if less than 2 cpu return a } @@ -196,7 +196,7 @@ func (a *PagedArray[T]) AllParallel(yield func(int, *T) bool) { book := a.book wg := new(sync.WaitGroup) - gorutineBudget := a.parallelCount + gorutineBudget := a.maxNumWorkers runner := func(data *[pageSize]T, offset int, startIndex int, wg *sync.WaitGroup) { defer wg.Done() @@ -269,74 +269,56 @@ func (a *PagedArray[T]) AllDataValue(yield func(T) bool) { } } -func (a *PagedArray[T]) AllDataValueParallel(yield func(T) bool) { - var page *ArrayPage[T] - var data *[pageSize]T - - book := a.book - wg := new(sync.WaitGroup) - gorutineBudget := a.parallelCount - runner := func(data *[pageSize]T, startIndex int, wg *sync.WaitGroup) { - defer wg.Done() - for j := startIndex; j >= 0; j-- { - if !yield((data[j])) { - return - } - } - } - - if a.len == 0 { - return - } +func (a *PagedArray[T]) AllDataValueParallel(batchSize int, yield func(T, int) bool) { + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) + chunkSize := a.len / numWorkers - wg.Add(int(a.currentPageIndex) + 1) - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - data = &page.data + var wg sync.WaitGroup - if gorutineBudget > 0 { - go runner(data, page.len-1, wg) - gorutineBudget-- - continue + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } - - runner(data, page.len-1, wg) + go func(start int, end int) { + defer wg.Done() + r := end - start + for i := range r { + if !yield(a.GetValue(i+startIndex), workedId) { + return + } + } + }(startIndex, endIndex) } wg.Wait() } -func (a *PagedArray[T]) AllDataParallel(yield func(*T) bool) { - var page *ArrayPage[T] - var data *[pageSize]T - - book := a.book - wg := new(sync.WaitGroup) - gorutineBudget := a.parallelCount - runner := func(data *[pageSize]T, startIndex int, wg *sync.WaitGroup) { - defer wg.Done() - for j := startIndex; j >= 0; j-- { - if !yield(&(data[j])) { - return - } - } - } - - if a.len == 0 { - return - } +func (a *PagedArray[T]) AllDataParallel(batchSize int, yield func(*T, int) bool) { + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) + chunkSize := a.len / numWorkers - wg.Add(int(a.currentPageIndex) + 1) - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - data = &page.data + var wg sync.WaitGroup - if gorutineBudget > 0 { - go runner(data, page.len-1, wg) - gorutineBudget-- - continue + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } - - runner(data, page.len-1, wg) + go func(start int, end int) { + defer wg.Done() + r := end - start + for i := range r { + if !yield(a.Get(i+startIndex), workedId) { + return + } + } + }(startIndex, endIndex) } wg.Wait() } diff --git a/pkg/ecs/slice.go b/pkg/ecs/slice.go index 7f8aceda..1baad235 100644 --- a/pkg/ecs/slice.go +++ b/pkg/ecs/slice.go @@ -16,8 +16,8 @@ import ( type Slice[T any] struct { data []T len int - wg sync.WaitGroup maxNumWorkers int + wg sync.WaitGroup } func NewSlice[T any](size int) (a Slice[T]) { @@ -55,7 +55,7 @@ func (a *Slice[T]) Set(index int, value T) *T { } func (a *Slice[T]) Append(values ...T) *T { - a.data = append(a.data[:a.len-1], values...) + a.data = append(a.data[:a.len], values...) a.len += len(values) return &a.data[a.len-1] } @@ -101,10 +101,8 @@ func (a *Slice[T]) Last() *T { return a.Get(index) } -func (a *Slice[T]) Raw(result []T) []T { - result = result[:0] - copy(result, a.data) - return result +func (a *Slice[T]) Raw() []T { + return a.data } func (a *Slice[T]) getPageIdAndIndex(index int) (int, int) { diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index 02bd7dfd..f6a1b163 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -40,14 +40,12 @@ type BvhComponent struct { } type BvhTree struct { - Nodes ecs.PagedArray[BvhNode] - AabbNodes ecs.PagedArray[AABB] - Leaves ecs.PagedArray[BvhLeaf] - AabbLeaves ecs.PagedArray[AABB] - Codes ecs.PagedArray[uint64] - Components ecs.PagedArray[BvhComponent] - - ComponentsSlice []BvhComponent + Nodes ecs.Slice[BvhNode] + AabbNodes ecs.Slice[AABB] + Leaves ecs.Slice[BvhLeaf] + AabbLeaves ecs.Slice[AABB] + Codes ecs.Slice[uint64] + Components ecs.Slice[BvhComponent] } func (t *BvhTree) AddComponent(entity ecs.Entity, aabb AABB) { @@ -152,20 +150,15 @@ func (t *BvhTree) Build() { t.AabbLeaves.Reset() t.Codes.Reset() - // Extract and sort components by morton code - if cap(t.ComponentsSlice) < t.Components.Len() { - t.ComponentsSlice = make([]BvhComponent, 0, t.Components.Len()) - } - - t.ComponentsSlice = t.Components.Raw(t.ComponentsSlice) + var sorted = t.Components.Raw() - slices.SortFunc(t.ComponentsSlice, func(a, b BvhComponent) int { + slices.SortFunc(sorted, func(a, b BvhComponent) int { return int(a.Code - b.Code) }) // Add leaves - for i := range t.ComponentsSlice { - component := &t.ComponentsSlice[i] + for i := range sorted { + component := sorted[i] t.Leaves.Append(BvhLeaf{Id: component.Entity}) t.AabbLeaves.Append(component.Aabb) t.Codes.Append(component.Code) @@ -187,14 +180,15 @@ func (t *BvhTree) Build() { childrenCreated bool } - stack := []buildTask{ + stack := [32]buildTask{ {parentIndex: 0, start: 0, end: t.Leaves.Len() - 1, childrenCreated: false}, } + stackLen := 1 - for len(stack) > 0 { + for stackLen > 0 { + stackLen-- // Pop the last task - task := stack[len(stack)-1] - stack = stack[:len(stack)-1] + task := stack[stackLen] if !task.childrenCreated { if task.start == task.end { @@ -215,28 +209,31 @@ func (t *BvhTree) Build() { t.Nodes.Get(task.parentIndex).ChildIndex = int32(leftIndex) // Push parent task back with childrenCreated=true - stack = append(stack, buildTask{ + stack[stackLen] = buildTask{ parentIndex: task.parentIndex, start: task.start, end: task.end, childrenCreated: true, - }) + } + stackLen++ // Push right child task (split+1 to end) - stack = append(stack, buildTask{ + stack[stackLen] = buildTask{ parentIndex: leftIndex + 1, start: split + 1, end: task.end, childrenCreated: false, - }) + } + stackLen++ // Push left child task (start to split) - stack = append(stack, buildTask{ + stack[stackLen] = buildTask{ parentIndex: leftIndex, start: task.start, end: split, childrenCreated: false, - }) + } + stackLen++ } else { // Merge children's AABBs into parent leftChildIndex := int(t.Nodes.Get(task.parentIndex).ChildIndex) diff --git a/stdsystems/animation-player.go b/stdsystems/animation-player.go index 9e418163..ec397316 100644 --- a/stdsystems/animation-player.go +++ b/stdsystems/animation-player.go @@ -28,7 +28,7 @@ func (s *AnimationPlayerSystem) Init() { } func (s *AnimationPlayerSystem) Run() { dt := time.Since(s.lastRunAt) - s.AnimationPlayers.EachComponentParallel(func(animation *stdcomponents.AnimationPlayer) bool { + s.AnimationPlayers.EachComponent(func(animation *stdcomponents.AnimationPlayer) bool { animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond assert.True(animation.FrameDuration > 0, "frame duration must be greater than 0") diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index 2a07ec7f..90b7dbd0 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -25,7 +25,7 @@ type AnimationSpriteMatrixSystem struct { func (s *AnimationSpriteMatrixSystem) Init() {} func (s *AnimationSpriteMatrixSystem) Run() { - s.AnimationPlayers.EachEntityParallel(func(e ecs.Entity) bool { + s.AnimationPlayers.EachEntity(func(e ecs.Entity) bool { animationPlayer := s.AnimationPlayers.Get(e) spriteMatrix := s.SpriteMatrixes.Get(e) if spriteMatrix == nil { diff --git a/stdsystems/bvh-tree.go b/stdsystems/bvh-tree.go index 7c52f496..f4e62f36 100644 --- a/stdsystems/bvh-tree.go +++ b/stdsystems/bvh-tree.go @@ -43,20 +43,15 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { t.AabbLeaves.Reset() t.Codes.Reset() - // Extract and sort components by morton code - if cap(t.ComponentsSlice) < t.Components.Len() { - t.ComponentsSlice = make([]stdcomponents.BvhComponent, 0, t.Components.Len()) - } - - t.ComponentsSlice = t.Components.Raw(t.ComponentsSlice) + var sorted = t.Components.Raw() - slices.SortFunc(t.ComponentsSlice, func(a, b stdcomponents.BvhComponent) int { + slices.SortFunc(sorted, func(a, b stdcomponents.BvhComponent) int { return int(a.Code - b.Code) }) // Add leaves - for i := range t.ComponentsSlice { - component := &t.ComponentsSlice[i] + for i := range sorted { + component := sorted[i] t.Leaves.Append(stdcomponents.BvhLeaf{Id: component.Entity}) t.AabbLeaves.Append(component.Aabb) t.Codes.Append(component.Code) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index f1106c74..b8712aeb 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -22,14 +22,10 @@ import ( "image/color" "math" "math/rand" - "sync" + "runtime" "time" ) -const ( - chunkScaleFactor = 32 -) - func NewCollisionDetectionSystem() CollisionDetectionSystem { return CollisionDetectionSystem{} } @@ -64,31 +60,44 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { } func (s *CollisionDetectionSystem) Destroy() {} func (s *CollisionDetectionSystem) setup() { + const batchSize = 1 << 14 + // Reset grids - s.CollisionGridComponentManager.EachEntity(func(entity ecs.Entity) bool { + s.CollisionGridComponentManager.EachEntityParallel(batchSize, func(entity ecs.Entity, workerId int) bool { grid := s.CollisionGridComponentManager.Get(entity) assert.NotNil(grid) - grid.Entities.Reset() grid.MinBounds = vectors.Vec2{ X: math.MaxFloat32, Y: math.MaxFloat32, } - return true }) - // Register all entities in grids - s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + // Accumulate used CollisionLayers + var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, runtime.NumCPU()-2) + s.GenericCollider.EachEntityParallel(batchSize*4, func(entity ecs.Entity, workerId int) bool { collider := s.GenericCollider.Get(entity) assert.NotNil(collider) - - gridEntity, exists := s.gridLookup[collider.Layer] + collisionLayerAccumulators[workerId] |= 1 << collider.Layer + return true + }) + collisionLayerAccumulator := stdcomponents.CollisionLayer(0) + for _, mask := range collisionLayerAccumulators { + collisionLayerAccumulator |= mask + } + + // For each bit in collisionLayerAccumulators, create a grid if it doesn't exist + for i := uint(0); i < 32; i++ { + if collisionLayerAccumulator&(1< Date: Thu, 10 Apr 2025 20:14:40 +0500 Subject: [PATCH 117/196] +cameras --- engine.go | 3 +- examples/new-api/components/ids.go | 4 +- examples/new-api/components/texture-circle.go | 36 ++ examples/new-api/components/texture-rect.go | 34 ++ examples/new-api/config/config.go | 5 + examples/new-api/entities/asteroid.go | 15 + examples/new-api/entities/bullet.go | 42 +- examples/new-api/entities/player.go | 2 +- examples/new-api/entities/satellite.go | 28 +- examples/new-api/entities/spaceship.go | 57 ++- examples/new-api/entities/wall.go | 17 +- examples/new-api/game.go | 27 +- examples/new-api/instances/component-list.go | 106 ++--- examples/new-api/instances/system-list.go | 64 +-- examples/new-api/scenes/assterodd-scene.go | 36 +- examples/new-api/sprites/player.go | 1 + examples/new-api/systems/asterodd.go | 38 +- examples/new-api/systems/camera-main.go | 123 ++++++ examples/new-api/systems/camera-minimap.go | 76 ++++ examples/new-api/systems/debug-info.go | 252 ++++++++++++ .../new-api/systems/position-to-sprite.go | 60 +++ examples/new-api/systems/render-assterodd.go | 375 ------------------ examples/new-api/systems/render-overlay.go | 81 ++++ examples/new-api/systems/space-spawner.go | 6 + examples/new-api/systems/spaceship-intents.go | 38 +- examples/new-api/systems/texture-circle.go | 63 +++ examples/new-api/systems/texture-rect.go | 54 +++ stdcomponents/aabb.go | 11 +- stdcomponents/camera.go | 123 ++++++ stdcomponents/frame-buffer-2d.go | 38 ++ stdcomponents/ids.go | 8 +- stdcomponents/position-smooth.go | 35 ++ stdcomponents/renderable.go | 9 +- stdcomponents/sprite-matrix.go | 3 + stdcomponents/sprite.go | 10 +- stdsystems/os-handler-system.go | 37 ++ stdsystems/render-2d-cameras.go | 105 +++++ stdsystems/render.go | 333 ++-------------- stdsystems/sprite-matrix.go | 9 +- stdsystems/sprite.go | 40 +- stdsystems/texture-position-smooth.go | 94 +++++ vectors/rectangle.go | 19 + 42 files changed, 1647 insertions(+), 870 deletions(-) create mode 100644 examples/new-api/components/texture-circle.go create mode 100644 examples/new-api/components/texture-rect.go create mode 100644 examples/new-api/systems/camera-main.go create mode 100644 examples/new-api/systems/camera-minimap.go create mode 100644 examples/new-api/systems/debug-info.go create mode 100644 examples/new-api/systems/position-to-sprite.go delete mode 100644 examples/new-api/systems/render-assterodd.go create mode 100644 examples/new-api/systems/render-overlay.go create mode 100644 examples/new-api/systems/texture-circle.go create mode 100644 examples/new-api/systems/texture-rect.go create mode 100644 stdcomponents/camera.go create mode 100644 stdcomponents/frame-buffer-2d.go create mode 100644 stdcomponents/position-smooth.go create mode 100644 stdsystems/os-handler-system.go create mode 100644 stdsystems/render-2d-cameras.go create mode 100644 stdsystems/texture-position-smooth.go create mode 100644 vectors/rectangle.go diff --git a/engine.go b/engine.go index c932b18d..694e346a 100644 --- a/engine.go +++ b/engine.go @@ -16,7 +16,6 @@ Thank you for your support! package gomp import ( - "log" "time" ) @@ -73,7 +72,7 @@ func (e *Engine) Run(tickrate uint, framerate uint) { } if loops >= MaxFrameSkips { nextFixedUpdateAt = time.Now() - log.Println("Too many updates detected") + //log.Println("Too many updates detected") } // RenderAssterodd diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go index 4c3c374a..74ccd2ba 100644 --- a/examples/new-api/components/ids.go +++ b/examples/new-api/components/ids.go @@ -19,7 +19,7 @@ import ( ) const ( - HealthComponentId = iota + stdcomponents.StdComponentIds + HealthComponentId = iota + stdcomponents.StdLastComponentId ControllerComponentId PlayerTagComponentId BulletTagComponentId @@ -30,4 +30,6 @@ const ( SpaceshipIntentComponentId AsteroidSceneManagerComponentId SoundEffectManagerComponentId + TextureRectComponentId + TextureCircleComponentId ) diff --git a/examples/new-api/components/texture-circle.go b/examples/new-api/components/texture-circle.go new file mode 100644 index 00000000..52a5d4c3 --- /dev/null +++ b/examples/new-api/components/texture-circle.go @@ -0,0 +1,36 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "image/color" +) + +type TextureCircle struct { + CenterX float32 + CenterY float32 + Radius float32 + Rotation float32 + Origin rl.Vector2 + Color color.RGBA +} + +type PrimitiveCircleComponentManager = ecs.ComponentManager[TextureCircle] + +func NewTextureCircleComponentManager() PrimitiveCircleComponentManager { + return ecs.NewComponentManager[TextureCircle](TextureCircleComponentId) +} diff --git a/examples/new-api/components/texture-rect.go b/examples/new-api/components/texture-rect.go new file mode 100644 index 00000000..66e45e7d --- /dev/null +++ b/examples/new-api/components/texture-rect.go @@ -0,0 +1,34 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "image/color" +) + +type TextureRect struct { + Dest rl.Rectangle + Origin rl.Vector2 + Rotation float32 + Color color.RGBA +} + +type TextureRectComponentManager = ecs.ComponentManager[TextureRect] + +func NewTextureRectComponentManager() TextureRectComponentManager { + return ecs.NewComponentManager[TextureRect](TextureRectComponentId) +} diff --git a/examples/new-api/config/config.go b/examples/new-api/config/config.go index 32bd3a2f..9614a64c 100644 --- a/examples/new-api/config/config.go +++ b/examples/new-api/config/config.go @@ -23,3 +23,8 @@ const ( EnemyCollisionLayer WallCollisionLayer ) + +const ( + MainCameraLayer stdcomponents.CameraLayer = 1 << iota + MinimapCameraLayer +) diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index 7fc72362..36a7a53b 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -37,6 +37,9 @@ type CreateAsteroidManagers struct { AsteroidTags *components.AsteroidComponentManager Hp *components.HpComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager + Renderables *stdcomponents.RenderableComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + Textures *stdcomponents.RLTextureProComponentManager } func CreateAsteroid( @@ -85,6 +88,12 @@ func CreateAsteroid( X: 32, Y: 32, }, + Dest: rl.Rectangle{ + X: posX, + Y: posY, + Width: 64 * scaleFactor, + Height: 64 * scaleFactor, + }, Tint: color.RGBA{ R: 255, G: 255, @@ -92,6 +101,8 @@ func CreateAsteroid( A: 255, }, }) + props.Textures.Create(e, stdcomponents.RLTexturePro{}) + props.RenderOrders.Create(e, stdcomponents.RenderOrder{}) props.AsteroidTags.Create(e, components.AsteroidTag{}) hp := int32(3 + rand.Intn(6)) props.Hp.Create(e, components.Hp{ @@ -102,6 +113,10 @@ func CreateAsteroid( IsStatic: false, Mass: 1, }) + props.Renderables.Create(e, stdcomponents.Renderable{ + Type: stdcomponents.SpriteRenderableType, + CameraMask: config.MainCameraLayer | config.MinimapCameraLayer, + }) return e } diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 30ae635c..028759b7 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -36,6 +36,10 @@ type CreateBulletManagers struct { Sprites *stdcomponents.SpriteComponentManager BulletTags *components.BulletTagComponentManager Hps *components.HpComponentManager + Smooth *stdcomponents.TexturePositionSmoothComponentManager + Renderables *stdcomponents.RenderableComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + Textures *stdcomponents.RLTextureProComponentManager } func CreateBullet( @@ -44,25 +48,25 @@ func CreateBullet( angle float64, velocityX, velocityY float32, ) ecs.Entity { - bullet := props.EntityManager.Create() - props.Positions.Create(bullet, stdcomponents.Position{ + entity := props.EntityManager.Create() + props.Positions.Create(entity, stdcomponents.Position{ XY: vectors.Vec2{ X: posX, Y: posY, }, }) - props.Rotations.Create(bullet, stdcomponents.Rotation{}.SetFromDegrees(angle)) - props.Scales.Create(bullet, stdcomponents.Scale{ + props.Rotations.Create(entity, stdcomponents.Rotation{}.SetFromDegrees(angle)) + props.Scales.Create(entity, stdcomponents.Scale{ XY: vectors.Vec2{ X: 1, Y: 1, }, }) - props.Velocities.Create(bullet, stdcomponents.Velocity{ + props.Velocities.Create(entity, stdcomponents.Velocity{ X: velocityX, Y: velocityY, }) - props.CircleColliders.Create(bullet, stdcomponents.CircleCollider{ + props.CircleColliders.Create(entity, stdcomponents.CircleCollider{ Radius: 6, Offset: vectors.Vec2{ X: 0, @@ -72,7 +76,7 @@ func CreateBullet( Mask: 1<= 8192 { - if len(s.instanceData) > 0 { - s.submitBatch(s.instanceData) - s.instanceData = s.instanceData[:0] - } - currentTex = entry.TextureId - } - s.instanceData = append(s.instanceData, s.getInstanceData(entry.Entity)) - } - s.submitBatch(s.instanceData) // Submit last batch - s.renderList = s.renderList[:0] - - // ========== - // DEBUG - // ========== - if s.debug { - rl.BeginMode2D(s.camera) - s.AABBs.EachEntity(func(e ecs.Entity) bool { - aabb := s.AABBs.Get(e) - clr := rl.Green - isSleeping := s.ColliderSleepStateComponentManager.Get(e) - if isSleeping != nil { - clr = rl.Blue - } - isTree := s.BvhTrees.Get(e) - if isTree != nil { - rl.DrawRectangle(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), isTree.Color) - return true - } - rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), clr) - return true - }) - s.Collisions.EachEntity(func(entity ecs.Entity) bool { - pos := s.Positions.Get(entity) - rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) - return true - }) - rl.EndMode2D() - } -} - -func (s *RenderAssteroddSystem) submitBatch(data []stdcomponents.RLTexturePro) { - rl.BeginMode2D(s.camera) - if s.debug { - for i := range data { - rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) - rl.DrawRectangle(int32(data[i].Dest.X-2), int32(data[i].Dest.Y-2), 4, 4, rl.Red) - } - } else { - for i := range data { - rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) - } - } - rl.EndMode2D() -} - -func (s *RenderAssteroddSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { - return *s.RlTexturePros.Get(e) -} - -func (s *RenderAssteroddSystem) prepareRender(dt time.Duration) { - wg := new(sync.WaitGroup) - wg.Add(6) - s.prepareAnimations(wg) - go s.prepareFlips(wg) - go s.preparePositions(wg, dt) - go s.prepareRotations(wg) - go s.prepareScales(wg) - go s.prepareTints(wg) - wg.Wait() -} - -func (s *RenderAssteroddSystem) prepareAnimations(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - animation := s.AnimationPlayers.Get(entity) - if animation == nil { - return true - } - frame := &texturePro.Frame - if animation.Vertical { - frame.Y += frame.Height * float32(animation.Current) - } else { - frame.X += frame.Width * float32(animation.Current) - } - return true - }) -} - -func (s *RenderAssteroddSystem) prepareFlips(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - flipped := s.Flips.Get(entity) - if flipped == nil { - return true - } - if flipped.X { - texturePro.Frame.Width *= -1 - } - if flipped.Y { - texturePro.Frame.Height *= -1 - } - return true - }) -} - -func (s *RenderAssteroddSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { - defer wg.Done() - dts := dt.Seconds() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - position := s.Positions.Get(entity) - if position == nil { - return true - } - decay := 40.0 // DECAY IS TICKRATE DEPENDENT - x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) - y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) - texturePro.Dest.X = x - texturePro.Dest.Y = y - player := s.Player.Get(entity) - if player != nil { - s.camera.Target.X = x - s.camera.Target.Y = y - } - - return true - }) -} - -func (s *RenderAssteroddSystem) prepareRotations(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - rotation := s.Rotations.Get(entity) - if rotation == nil { - return true - } - texturePro.Rotation = float32(rotation.Degrees()) - return true - }) -} - -func (s *RenderAssteroddSystem) prepareScales(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - scale := s.Scales.Get(entity) - if scale == nil { - return true - } - texturePro.Dest.Width *= scale.XY.X - texturePro.Dest.Height *= scale.XY.Y - return true - }) -} - -func (s *RenderAssteroddSystem) prepareTints(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - tr := s.RlTexturePros.Get(entity) - tint := s.Tints.Get(entity) - if tint == nil { - return true - } - trTint := &tr.Tint - trTint.A = tint.A - trTint.R = tint.R - trTint.G = tint.G - trTint.B = tint.B - return true - }) -} - -func (s *RenderAssteroddSystem) expDecay(a, b, decay, dt float64) float64 { - return b + (a-b)*(math.Exp(-decay*dt)) -} diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go new file mode 100644 index 00000000..78ce865b --- /dev/null +++ b/examples/new-api/systems/render-overlay.go @@ -0,0 +1,81 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package systems + +import ( + "fmt" + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/components" + "gomp/examples/new-api/config" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewRenderOverlaySystem() RenderOverlaySystem { + return RenderOverlaySystem{} +} + +type RenderOverlaySystem struct { + EntityManager *ecs.EntityManager + SceneManager *components.AsteroidSceneManagerComponentManager + Cameras *stdcomponents.CameraComponentManager + FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager + monitorWidth int + monitorHeight int +} + +func (s *RenderOverlaySystem) Init() { + s.monitorWidth = rl.GetScreenWidth() + s.monitorHeight = rl.GetScreenHeight() +} + +func (s *RenderOverlaySystem) Run(dt time.Duration) bool { + + s.FrameBuffer2D.EachComponent(func(c *stdcomponents.FrameBuffer2D) bool { + switch c.Layer { + case config.MainCameraLayer: + rl.BeginTextureMode(c.Texture) + // Print the current FPS + rl.DrawRectangleRec(rl.Rectangle{Height: 100, Width: 200}, rl.Black) + rl.DrawFPS(10, 10) + rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) + // Game over + s.SceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { + rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), 10, 50, 20, rl.RayWhite) + rl.DrawText(fmt.Sprintf("Score: %d", a.PlayerScore), 10, 70, 20, rl.RayWhite) + if a.PlayerHp <= 0 { + text := "Game Over" + textSize := rl.MeasureTextEx(rl.GetFontDefault(), text, 96, 0) + x := (s.monitorWidth - int(textSize.X)) / 2 + y := (s.monitorHeight - int(textSize.Y)) / 2 + rl.DrawText(text, int32(x), int32(y), 96, rl.Red) + + } + return false + }) + rl.EndTextureMode() + case config.MinimapCameraLayer: + rl.BeginTextureMode(c.Texture) + rl.DrawRectangleLines(1, 1, c.Texture.Texture.Width-1, c.Texture.Texture.Height-1, rl.Green) + rl.EndTextureMode() + } + return true + }) + + return true +} + +func (s *RenderOverlaySystem) Destroy() {} diff --git a/examples/new-api/systems/space-spawner.go b/examples/new-api/systems/space-spawner.go index 6bce6b8c..360734a6 100644 --- a/examples/new-api/systems/space-spawner.go +++ b/examples/new-api/systems/space-spawner.go @@ -39,6 +39,9 @@ type SpaceSpawnerSystem struct { Rotations *stdcomponents.RotationComponentManager Scales *stdcomponents.ScaleComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager + Renderables *stdcomponents.RenderableComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + Textures *stdcomponents.RLTextureProComponentManager } func (s *SpaceSpawnerSystem) Init() {} @@ -69,6 +72,9 @@ func (s *SpaceSpawnerSystem) Run(dt time.Duration) { AsteroidTags: s.Asteroids, Hp: s.Hp, RigidBodies: s.RigidBodies, + Renderables: s.Renderables, + RenderOrders: s.RenderOrders, + Textures: s.Textures, }, pos.XY.X, pos.XY.Y, 0, 1+rand.Float32()*2, 0, 50+rand.Float32()*100) spawner.CooldownLeft = spawner.Cooldown return true diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index 62a32e3a..fcc2b2fc 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -30,21 +30,25 @@ func NewSpaceshipIntentsSystem() SpaceshipIntentsSystem { } type SpaceshipIntentsSystem struct { - EntityManager *ecs.EntityManager - SpaceshipIntents *components.SpaceshipIntentComponentManager - Positions *stdcomponents.PositionComponentManager - Velocities *stdcomponents.VelocityComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - CircleColliders *stdcomponents.CircleColliderComponentManager - BulletTags *components.BulletTagComponentManager - Sprites *stdcomponents.SpriteComponentManager - RigidBodies *stdcomponents.RigidBodyComponentManager - Weapons *components.WeaponComponentManager - Hps *components.HpComponentManager - SoundEffects *components.SoundEffectsComponentManager - moveSpeed float32 + EntityManager *ecs.EntityManager + SpaceshipIntents *components.SpaceshipIntentComponentManager + Positions *stdcomponents.PositionComponentManager + Velocities *stdcomponents.VelocityComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + BulletTags *components.BulletTagComponentManager + Sprites *stdcomponents.SpriteComponentManager + Textures *stdcomponents.RLTextureProComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager + Weapons *components.WeaponComponentManager + Hps *components.HpComponentManager + SoundEffects *components.SoundEffectsComponentManager + TexturePositionSmooth *stdcomponents.TexturePositionSmoothComponentManager + Renderables *stdcomponents.RenderableComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + moveSpeed float32 } func (s *SpaceshipIntentsSystem) Init() {} @@ -123,8 +127,12 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { CircleColliders: s.CircleColliders, RigidBodies: s.RigidBodies, Sprites: s.Sprites, + Textures: s.Textures, BulletTags: s.BulletTags, Hps: s.Hps, + Smooth: s.TexturePositionSmooth, + Renderables: s.Renderables, + RenderOrders: s.RenderOrders, }, pos.XY.X, pos.XY.Y, angle, bulletVelocityX, bulletVelocityY) } weapon.CooldownLeft = weapon.Cooldown diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go new file mode 100644 index 00000000..bcd2f297 --- /dev/null +++ b/examples/new-api/systems/texture-circle.go @@ -0,0 +1,63 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package systems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureCircleSystem() TextureCircleSystem { + return TextureCircleSystem{} +} + +type TextureCircleSystem struct { + Circles *components.PrimitiveCircleComponentManager + Textures *stdcomponents.RLTextureProComponentManager + texture rl.RenderTexture2D +} + +func (s *TextureCircleSystem) Init() { + const circleRadius = 128 + var texture = rl.LoadRenderTexture(circleRadius*2, circleRadius*2) + rl.BeginTextureMode(texture) + rl.DrawCircle(circleRadius, circleRadius, circleRadius, rl.White) + rl.EndTextureMode() + s.texture = texture +} + +func (s *TextureCircleSystem) Run(dt time.Duration) { + s.Circles.EachEntityParallel(func(entity ecs.Entity) bool { + circle := s.Circles.Get(entity) + texture := s.Textures.Get(entity) + assert.Nil(texture, "texture is nil; entity: %d", entity) + + texture.Texture = &s.texture.Texture + texture.Dest.X = circle.CenterX - circle.Radius + texture.Dest.Y = circle.CenterY - circle.Radius + texture.Dest.Width = circle.Radius * 2 + texture.Dest.Height = circle.Radius * 2 + texture.Frame = rl.Rectangle{ + X: 0, + Y: 0, + Width: float32(s.texture.Texture.Width), + Height: float32(s.texture.Texture.Height), + } + texture.Rotation = circle.Rotation + texture.Origin = circle.Origin + texture.Tint = circle.Color + return true + }) +} + +func (s *TextureCircleSystem) Destroy() { + rl.UnloadRenderTexture(s.texture) +} diff --git a/examples/new-api/systems/texture-rect.go b/examples/new-api/systems/texture-rect.go new file mode 100644 index 00000000..5408a889 --- /dev/null +++ b/examples/new-api/systems/texture-rect.go @@ -0,0 +1,54 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package systems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRectSystem() TextureRectSystem { + return TextureRectSystem{} +} + +type TextureRectSystem struct { + TextureRect *components.TextureRectComponentManager + Textures *stdcomponents.RLTextureProComponentManager + texture rl.RenderTexture2D +} + +func (s *TextureRectSystem) Init() { + var texture = rl.LoadRenderTexture(1, 1) // :) + rl.BeginTextureMode(texture) + rl.ClearBackground(rl.White) + rl.EndTextureMode() + s.texture = texture +} + +func (s *TextureRectSystem) Run(dt time.Duration) { + // Create shallow copy of texture to draw rectangles + s.TextureRect.EachEntityParallel(func(entity ecs.Entity) bool { + rect := s.TextureRect.Get(entity) + texture := s.Textures.Get(entity) + assert.Nil(texture, "texture is nil; entity: %d", entity) + + texture.Texture = &s.texture.Texture + texture.Dest = rect.Dest + texture.Rotation = rect.Rotation + texture.Origin = rect.Origin + texture.Tint = rect.Color + return true + }) +} + +func (s *TextureRectSystem) Destroy() { + rl.UnloadRenderTexture(s.texture) +} diff --git a/stdcomponents/aabb.go b/stdcomponents/aabb.go index c2ddfd9e..fd3df068 100644 --- a/stdcomponents/aabb.go +++ b/stdcomponents/aabb.go @@ -24,10 +24,19 @@ type AABB struct { Max vectors.Vec2 } -func (a *AABB) Center() vectors.Vec2 { +func (a AABB) Center() vectors.Vec2 { return a.Min.Add(a.Max).Scale(0.5) } +func (a AABB) Rect() vectors.Rectangle { + return vectors.Rectangle{ + X: a.Min.X, + Y: a.Min.Y, + Width: a.Max.X - a.Min.X, + Height: a.Max.Y - a.Min.Y, + } +} + type AABBComponentManager = ecs.ComponentManager[AABB] func NewAABBComponentManager() AABBComponentManager { diff --git a/stdcomponents/camera.go b/stdcomponents/camera.go new file mode 100644 index 00000000..c8efe378 --- /dev/null +++ b/stdcomponents/camera.go @@ -0,0 +1,123 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/vectors" + "image/color" + "math" +) + +const ( + Culling2DFullscreenBB uint8 = iota + Culling2DNone // TODO: set default? + // Culling2DDistance + // Culling2DRectangle + // Culling2DOcclusion difficult to implement + // Culling2DFrustum huh +) + +type CameraLayer uint64 + +// Camera component, defines a camera component +// Camera2D is a struct that defines a 2D camera +// Dst defines the camera's destination on the renderer screen +// Layer defines the camera's layer, default is 0 - disabled +// Order defines the camera's order, ascending order +type Camera struct { + rl.Camera2D + Dst vectors.Rectangle // TODO: remove? + Layer CameraLayer + Culling uint8 + Order int + BlendMode rl.BlendMode + BGColor color.RGBA + Tint color.RGBA +} + +func (c Camera) Rect() vectors.Rectangle { + // Calculate the non-rotated top-left corner of the view rectangle + x := c.Target.X - (c.Offset.X / c.Zoom) + y := c.Target.Y - (c.Offset.Y / c.Zoom) + width := c.Offset.X * 2 / c.Zoom + height := c.Offset.Y * 2 / c.Zoom + + // When rotation is zero, we can return directly. + if c.Rotation == 0 { + return vectors.Rectangle{ + X: x, + Y: y, + Width: width, + Height: height, + } + } + + // Define the four corners of the non-rotated rectangle + topLeft := vectors.Vec2{X: x, Y: y} + topRight := vectors.Vec2{X: x + width, Y: y} + bottomRight := vectors.Vec2{X: x + width, Y: y + height} + bottomLeft := vectors.Vec2{X: x, Y: y + height} + + // Rotate each corner around the camera.Target using the camera rotation + topLeft = rotatePoint(topLeft, vectors.Vec2(c.Target), float64(c.Rotation)) + topRight = rotatePoint(topRight, vectors.Vec2(c.Target), float64(c.Rotation)) + bottomRight = rotatePoint(bottomRight, vectors.Vec2(c.Target), float64(c.Rotation)) + bottomLeft = rotatePoint(bottomLeft, vectors.Vec2(c.Target), float64(c.Rotation)) + + // Determine the axis-aligned bounding box that contains all rotated points + // TODO: fast math 32bit + minX := math.Min(math.Min(float64(topLeft.X), float64(topRight.X)), math.Min(float64(bottomRight.X), float64(bottomLeft.X))) + maxX := math.Max(math.Max(float64(topLeft.X), float64(topRight.X)), math.Max(float64(bottomRight.X), float64(bottomLeft.X))) + minY := math.Min(math.Min(float64(topLeft.Y), float64(topRight.Y)), math.Min(float64(bottomRight.Y), float64(bottomLeft.Y))) + maxY := math.Max(math.Max(float64(topLeft.Y), float64(topRight.Y)), math.Max(float64(bottomRight.Y), float64(bottomLeft.Y))) + + return vectors.Rectangle{ + X: float32(minX), + Y: float32(minY), + Width: float32(maxX - minX), + Height: float32(maxY - minY), + } +} + +// rotatePoint rotates point p around pivot by angle degrees. +func rotatePoint(p, pivot vectors.Vec2, angle float64) vectors.Vec2 { + // Convert angle from degrees to radians. + // TODO: fast math 32bit + theta := angle * (math.Pi / 180) + sinTheta := float32(math.Sin(theta)) + cosTheta := float32(math.Cos(theta)) + + // Translate point to origin + dx := p.X - pivot.X + dy := p.Y - pivot.Y + + // Apply rotation matrix + rotatedX := dx*cosTheta - dy*sinTheta + rotatedY := dx*sinTheta + dy*cosTheta + + // Translate point back + return vectors.Vec2{ + X: rotatedX + pivot.X, + Y: rotatedY + pivot.Y, + } +} + +type CameraComponentManager = ecs.ComponentManager[Camera] + +func NewCameraComponentManager() CameraComponentManager { + return ecs.NewComponentManager[Camera](CameraComponentId) +} diff --git a/stdcomponents/frame-buffer-2d.go b/stdcomponents/frame-buffer-2d.go new file mode 100644 index 00000000..aa87e53a --- /dev/null +++ b/stdcomponents/frame-buffer-2d.go @@ -0,0 +1,38 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "image/color" +) + +type FrameBuffer2D struct { + Position rl.Vector2 + Frame rl.Rectangle + Texture rl.RenderTexture2D + Layer CameraLayer + BlendMode rl.BlendMode + Rotation float32 + Tint color.RGBA + Dst rl.Rectangle +} + +type FrameBuffer2DComponentManager = ecs.ComponentManager[FrameBuffer2D] + +func NewFrameBuffer2DComponentManager() FrameBuffer2DComponentManager { + return ecs.NewComponentManager[FrameBuffer2D](FrameBuffer2DComponentId) +} diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 71463f75..36ca6051 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -10,7 +10,7 @@ import ( "gomp/pkg/ecs" ) -// StdComponentIds MUST always be the last +// StdLastComponentId MUST always be the last const ( InvalidComponentId ecs.ComponentId = iota TransformComponentId @@ -40,5 +40,9 @@ const ( AABBComponentId RigidBodyComponentId BvhTreeComponentId - StdComponentIds + FrameBuffer2DComponentId + CameraComponentId + TexturePositionSmoothComponentId + + StdLastComponentId ) diff --git a/stdcomponents/position-smooth.go b/stdcomponents/position-smooth.go new file mode 100644 index 00000000..5bcd7614 --- /dev/null +++ b/stdcomponents/position-smooth.go @@ -0,0 +1,35 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" +) + +const ( + TexturePositionSmoothOff TexturePositionSmooth = iota + TexturePositionSmoothLerp + TexturePositionSmoothExpDecay +) + +// TexturePositionSmooth is the component tag for stdsystems.TexturePositionSmoothSystem +// TODO: refactor or make stable realization +type TexturePositionSmooth uint8 + +type TexturePositionSmoothComponentManager = ecs.ComponentManager[TexturePositionSmooth] + +func NewTexturePositionSmoothComponentManager() TexturePositionSmoothComponentManager { + return ecs.NewComponentManager[TexturePositionSmooth](TexturePositionSmoothComponentId) +} diff --git a/stdcomponents/renderable.go b/stdcomponents/renderable.go index 0c84dcc7..8ed226ed 100644 --- a/stdcomponents/renderable.go +++ b/stdcomponents/renderable.go @@ -17,12 +17,17 @@ package stdcomponents import "gomp/pkg/ecs" const ( - InvalidRenderableType Renderable = iota + InvalidRenderableType RenderableType = iota SpriteRenderableType SpriteMatrixRenderableType ) -type Renderable uint8 +type Renderable struct { + CameraMask CameraLayer + Type RenderableType +} + +type RenderableType uint8 type RenderableComponentManager = ecs.ComponentManager[Renderable] diff --git a/stdcomponents/sprite-matrix.go b/stdcomponents/sprite-matrix.go index 5db7fa96..e2afa6a3 100644 --- a/stdcomponents/sprite-matrix.go +++ b/stdcomponents/sprite-matrix.go @@ -17,6 +17,7 @@ package stdcomponents import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" + "gomp/vectors" ) type SpriteMatrixAnimation struct { @@ -30,8 +31,10 @@ type SpriteMatrixAnimation struct { type SpriteMatrix struct { Texture *rl.Texture2D Origin rl.Vector2 + Dest rl.Rectangle FPS int32 Animations []SpriteMatrixAnimation + Rotation vectors.Radians } type SpriteMatrixComponentManager = ecs.SharedComponentManager[SpriteMatrix] diff --git a/stdcomponents/sprite.go b/stdcomponents/sprite.go index 80e9ead4..54f1153c 100644 --- a/stdcomponents/sprite.go +++ b/stdcomponents/sprite.go @@ -21,10 +21,12 @@ import ( ) type Sprite struct { - Texture *rl.Texture2D - Frame rl.Rectangle - Origin rl.Vector2 - Tint color.RGBA + Texture *rl.Texture2D + Frame rl.Rectangle + Origin rl.Vector2 + Tint color.RGBA + Dest rl.Rectangle + Rotation float32 } type SpriteComponentManager = ecs.ComponentManager[Sprite] diff --git a/stdsystems/os-handler-system.go b/stdsystems/os-handler-system.go new file mode 100644 index 00000000..4a99dd42 --- /dev/null +++ b/stdsystems/os-handler-system.go @@ -0,0 +1,37 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "time" +) + +func NewOSHandlerSystem() OSHandlerSystem { + return OSHandlerSystem{} +} + +type OSHandlerSystem struct{} + +func (s *OSHandlerSystem) Init() { + // TODO: pass parameters, resize or reinit. + rl.InitWindow(1280, 720, "GOMP") +} + +func (s *OSHandlerSystem) Run(dt time.Duration) bool { + if rl.IsKeyPressed(rl.KeyEscape) { + return true + } + if rl.WindowShouldClose() { + return true + } + return false +} + +func (s *OSHandlerSystem) Destroy() { + rl.CloseWindow() +} diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go new file mode 100644 index 00000000..fc177647 --- /dev/null +++ b/stdsystems/render-2d-cameras.go @@ -0,0 +1,105 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "cmp" + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "slices" + "time" +) + +func NewRender2DCamerasSystem() Render2DCamerasSystem { + return Render2DCamerasSystem{} +} + +type Render2DCamerasSystem struct { + Renderables *stdcomponents.RenderableComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + Textures *stdcomponents.RLTextureProComponentManager + AABBs *stdcomponents.AABBComponentManager + Cameras *stdcomponents.CameraComponentManager + RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager + renderObjects []renderObject +} + +type renderObject struct { + texture *stdcomponents.RLTexturePro + renderable *stdcomponents.Renderable + aabb *stdcomponents.AABB + order float32 +} + +func (s *Render2DCamerasSystem) Init() { + s.renderObjects = make([]renderObject, 0, s.Renderables.Len()) +} + +func (s *Render2DCamerasSystem) Run(dt time.Duration) { + s.Cameras.EachEntity(func(entity ecs.Entity) bool { + camera := s.Cameras.Get(entity) + renderTexture := s.RenderTexture2D.Get(entity) + + // Collect and sort render objects + s.Renderables.EachEntity(func(entity ecs.Entity) bool { + r := s.Renderables.Get(entity) + t := s.Textures.Get(entity) + o := s.RenderOrders.Get(entity) + aabb := s.AABBs.Get(entity) + s.renderObjects = append(s.renderObjects, renderObject{ + texture: t, + renderable: r, + order: o.CalculatedZ, + aabb: aabb, + }) + return true + }) + slices.SortFunc(s.renderObjects, func(a, b renderObject) int { + return cmp.Compare(a.order, b.order) + }) + + // Draw render objects + rl.BeginTextureMode(renderTexture.Texture) + rl.BeginMode2D(camera.Camera2D) + rl.ClearBackground(camera.BGColor) + + cameraRect := camera.Rect() + for _, obj := range s.renderObjects { + if camera.Layer&obj.renderable.CameraMask != 0 { + assert.Nil(obj.texture, "EntityTexturePro is nil") + switch camera.Culling { + case stdcomponents.Culling2DFullscreenBB: + if obj.aabb != nil && intersects(cameraRect, obj.aabb.Rect()) { + rl.DrawTexturePro(*obj.texture.Texture, obj.texture.Frame, obj.texture.Dest, obj.texture.Origin, obj.texture.Rotation, obj.texture.Tint) + } + default: + rl.DrawTexturePro(*obj.texture.Texture, obj.texture.Frame, obj.texture.Dest, obj.texture.Origin, obj.texture.Rotation, obj.texture.Tint) + } + + } + } + + rl.EndMode2D() + rl.EndTextureMode() + + s.renderObjects = s.renderObjects[:0] + return true + }) +} + +func intersects(rect1, rect2 vectors.Rectangle) bool { + return rect1.X < rect2.X+rect2.Width && + rect1.X+rect1.Width > rect2.X && + rect1.Y < rect2.Y+rect2.Height && + rect1.Y+rect1.Height > rect2.Y +} + +func (s *Render2DCamerasSystem) Destroy() { +} diff --git a/stdsystems/render.go b/stdsystems/render.go index ffab1536..f9c366f2 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -15,20 +15,15 @@ Thank you for your support! package stdsystems import ( - "fmt" rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" "gomp/stdcomponents" - "math" "slices" - "sync" "time" ) func NewRenderSystem() RenderSystem { - return RenderSystem{ - instanceData: make([]stdcomponents.RLTexturePro, 0, 8192), - } + return RenderSystem{} } type RenderSystem struct { @@ -51,10 +46,11 @@ type RenderSystem struct { Collisions *stdcomponents.CollisionComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager BvhTrees *stdcomponents.BvhTreeComponentManager + CamerasManager *stdcomponents.CameraComponentManager + FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager - renderList []renderEntry - instanceData []stdcomponents.RLTexturePro - camera rl.Camera2D + renderTextures []rl.RenderTexture2D + frames []stdcomponents.FrameBuffer2D monitorWidth int monitorHeight int @@ -62,48 +58,49 @@ type RenderSystem struct { debug bool } -type renderEntry struct { - Entity ecs.Entity - TextureId int - ZIndex float32 -} - func (s *RenderSystem) Init() { - rl.InitWindow(1280, 720, "GOMP") s.monitorWidth = rl.GetScreenWidth() s.monitorHeight = rl.GetScreenHeight() - s.camera = rl.Camera2D{ - Target: rl.NewVector2(float32(s.monitorWidth/2), float32(s.monitorHeight/2)), - Offset: rl.NewVector2(float32(s.monitorWidth/2), float32(s.monitorHeight/2)), - Rotation: 0, - Zoom: 1, - } + s.renderTextures = make([]rl.RenderTexture2D, 0, s.CamerasManager.Len()) + s.frames = make([]stdcomponents.FrameBuffer2D, 0, s.FrameBuffer2D.Len()) } func (s *RenderSystem) Run(dt time.Duration) bool { - if rl.WindowShouldClose() { - return true - } - - if rl.IsKeyPressed(rl.KeyF12) { - s.debug = !s.debug - } - s.prepareRender(dt) + s.FrameBuffer2D.EachComponent(func(c *stdcomponents.FrameBuffer2D) bool { + s.frames = append(s.frames, *c) + return true + }) + slices.SortFunc(s.frames, func(a, b stdcomponents.FrameBuffer2D) int { + return int(a.Layer - b.Layer) + }) rl.BeginDrawing() - rl.ClearBackground(rl.Black) - s.render() - rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) + for _, texture := range s.frames { + rl.BeginBlendMode(texture.BlendMode) + rl.DrawTexturePro(texture.Texture.Texture, + rl.Rectangle{ + X: 0, + Y: 0, + Width: float32(texture.Texture.Texture.Width), + Height: -float32(texture.Texture.Texture.Height), + }, + texture.Dst, + rl.Vector2{}, + texture.Rotation, + texture.Tint, + ) + rl.EndBlendMode() + } + rl.EndDrawing() + s.frames = s.frames[:0] return false } func (s *RenderSystem) Destroy() { - rl.CloseWindow() } type RenderInjector struct { @@ -126,6 +123,8 @@ type RenderInjector struct { Collisions *stdcomponents.CollisionComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager BvhTrees *stdcomponents.BvhTreeComponentManager + Camera *stdcomponents.CameraComponentManager + RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager } func (s *RenderSystem) InjectWorld(injector *RenderInjector) { @@ -148,266 +147,6 @@ func (s *RenderSystem) InjectWorld(injector *RenderInjector) { s.Collisions = injector.Collisions s.ColliderSleepStateComponentManager = injector.ColliderSleepStateComponentManager s.BvhTrees = injector.BvhTrees -} - -func (s *RenderSystem) render() { - // ========== - // DEBUG - // ========== - if s.debug { - rl.BeginMode2D(s.camera) - s.BoxColliders.EachEntity(func(e ecs.Entity) bool { - col := s.BoxColliders.Get(e) - scale := s.Scales.Get(e) - pos := s.Positions.Get(e) - rot := s.Rotations.Get(e) - - rl.DrawRectanglePro(rl.Rectangle{ - X: pos.XY.X, - Y: pos.XY.Y, - Width: col.WH.X * scale.XY.X, - Height: col.WH.Y * scale.XY.Y, - }, rl.Vector2{ - X: col.Offset.X * scale.XY.X, - Y: col.Offset.Y * scale.XY.Y, - }, float32(rot.Degrees()), rl.DarkGreen) - return true - }) - s.CircleColliders.EachEntity(func(e ecs.Entity) bool { - col := s.CircleColliders.Get(e) - scale := s.Scales.Get(e) - pos := s.Positions.Get(e) - - color := rl.DarkGreen - isSleeping := s.ColliderSleepStateComponentManager.Get(e) - if isSleeping != nil { - color = rl.Blue - } - - posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) - rl.DrawCircle(int32(posWithOffset.X), int32(posWithOffset.Y), col.Radius*scale.XY.X, color) - return true - }) - rl.EndMode2D() - } - - // Extract and sort entities - if cap(s.renderList) < s.Renderables.Len() { - s.renderList = append(s.renderList, make([]renderEntry, 0, s.Renderables.Len()-cap(s.renderList))...) - } - s.Renderables.EachEntity(func(e ecs.Entity) bool { - renderOrder := s.RenderOrders.Get(e) - - spriteMatrix := s.SpriteMatrixes.Get(e) - if spriteMatrix != nil { - s.renderList = append(s.renderList, renderEntry{ - Entity: e, - TextureId: int(spriteMatrix.Texture.ID), - ZIndex: renderOrder.CalculatedZ, - }) - return true - } - - sprite := s.Sprites.Get(e) - if sprite != nil { - s.renderList = append(s.renderList, renderEntry{ - Entity: e, - TextureId: int(sprite.Texture.ID), - ZIndex: renderOrder.CalculatedZ, - }) - return true - } - - panic("Unknown renderable type") - }) - - slices.SortStableFunc(s.renderList, func(a, b renderEntry) int { - if a.TextureId == b.TextureId { - return int(math.Floor(float64(a.ZIndex - b.ZIndex))) - } - return int(a.TextureId - b.TextureId) - }) - - // Batch and render - var currentTex = -1 - for i := range s.renderList { - entry := &s.renderList[i] - if entry.TextureId != currentTex || len(s.instanceData) >= 8192 { - if len(s.instanceData) > 0 { - s.submitBatch(s.instanceData) - s.instanceData = s.instanceData[:0] - } - currentTex = entry.TextureId - } - s.instanceData = append(s.instanceData, s.getInstanceData(entry.Entity)) - } - s.submitBatch(s.instanceData) // Submit last batch - s.renderList = s.renderList[:0] - - // ========== - // DEBUG - // ========== - if s.debug { - rl.BeginMode2D(s.camera) - s.AABBs.EachEntity(func(e ecs.Entity) bool { - aabb := s.AABBs.Get(e) - clr := rl.Green - isSleeping := s.ColliderSleepStateComponentManager.Get(e) - if isSleeping != nil { - clr = rl.Blue - } - isTree := s.BvhTrees.Get(e) - if isTree != nil { - rl.DrawRectangle(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), isTree.Color) - return true - } - rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), clr) - return true - }) - s.Collisions.EachEntity(func(entity ecs.Entity) bool { - pos := s.Positions.Get(entity) - rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) - return true - }) - rl.EndMode2D() - } -} - -func (s *RenderSystem) submitBatch(data []stdcomponents.RLTexturePro) { - rl.BeginMode2D(s.camera) - if s.debug { - for i := range data { - rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) - rl.DrawRectangle(int32(data[i].Dest.X-2), int32(data[i].Dest.Y-2), 4, 4, rl.Red) - } - } else { - for i := range data { - rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) - } - } - rl.EndMode2D() -} - -func (s *RenderSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { - return *s.RlTexturePros.Get(e) -} - -func (s *RenderSystem) prepareRender(dt time.Duration) { - wg := new(sync.WaitGroup) - wg.Add(6) - s.prepareAnimations(wg) - go s.prepareFlips(wg) - go s.preparePositions(wg, dt) - go s.prepareRotations(wg) - go s.prepareScales(wg) - go s.prepareTints(wg) - wg.Wait() -} - -func (s *RenderSystem) prepareAnimations(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - animation := s.AnimationPlayers.Get(entity) - if animation == nil { - return true - } - frame := &texturePro.Frame - if animation.Vertical { - frame.Y += frame.Height * float32(animation.Current) - } else { - frame.X += frame.Width * float32(animation.Current) - } - return true - }) -} - -func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - flipped := s.Flips.Get(entity) - if flipped == nil { - return true - } - if flipped.X { - texturePro.Frame.Width *= -1 - } - if flipped.Y { - texturePro.Frame.Height *= -1 - } - return true - }) -} - -func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { - defer wg.Done() - dts := dt.Seconds() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - position := s.Positions.Get(entity) - if position == nil { - return true - } - decay := 40.0 // DECAY IS TICKRATE DEPENDENT - x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) - y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) - texturePro.Dest.X = x - texturePro.Dest.Y = y - //player := s.Player.Get(entity) - //if player != nil { - // s.camera.Target.X = x - // s.camera.Target.Y = y - //} - - return true - }) -} - -func (s *RenderSystem) prepareRotations(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - rotation := s.Rotations.Get(entity) - if rotation == nil { - return true - } - texturePro.Rotation = float32(rotation.Degrees()) - return true - }) -} - -func (s *RenderSystem) prepareScales(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - scale := s.Scales.Get(entity) - if scale == nil { - return true - } - texturePro.Dest.Width *= scale.XY.X - texturePro.Dest.Height *= scale.XY.Y - return true - }) -} - -func (s *RenderSystem) prepareTints(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - tr := s.RlTexturePros.Get(entity) - tint := s.Tints.Get(entity) - if tint == nil { - return true - } - trTint := &tr.Tint - trTint.A = tint.A - trTint.R = tint.R - trTint.G = tint.G - trTint.B = tint.B - return true - }) -} - -func (s *RenderSystem) expDecay(a, b, decay, dt float64) float64 { - return b + (a-b)*(math.Exp(-decay*dt)) + s.CamerasManager = injector.Camera + s.FrameBuffer2D = injector.RenderTexture2D } diff --git a/stdsystems/sprite-matrix.go b/stdsystems/sprite-matrix.go index c92d6156..b629f657 100644 --- a/stdsystems/sprite-matrix.go +++ b/stdsystems/sprite-matrix.go @@ -7,7 +7,6 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdsystems import ( - rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" "gomp/stdcomponents" ) @@ -21,14 +20,12 @@ type SpriteMatrixSystem struct { SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager RLTexturePros *stdcomponents.RLTextureProComponentManager AnimationStates *stdcomponents.AnimationStateComponentManager - Positions *stdcomponents.PositionComponentManager } func (s *SpriteMatrixSystem) Init() {} func (s *SpriteMatrixSystem) Run() { s.SpriteMatrixes.EachEntity(func(entity ecs.Entity) bool { - spriteMatrix := s.SpriteMatrixes.Get(entity) // - position := s.Positions.Get(entity) + spriteMatrix := s.SpriteMatrixes.Get(entity) // animationState := s.AnimationStates.Get(entity) // frame := spriteMatrix.Animations[*animationState].Frame @@ -39,11 +36,11 @@ func (s *SpriteMatrixSystem) Run() { Texture: spriteMatrix.Texture, // Frame: frame, // Origin: spriteMatrix.Origin, - Dest: rl.Rectangle{X: position.XY.X, Y: position.XY.Y, Width: frame.Width, Height: frame.Height}, // + Dest: spriteMatrix.Dest, // }) } else { // Run spriteRender - + tr.Dest = spriteMatrix.Dest tr.Frame = frame } return true diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index 2ea0389f..f55f8d72 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -26,7 +26,6 @@ func NewSpriteSystem() SpriteSystem { // SpriteSystem is a system that prepares Sprite to be rendered type SpriteSystem struct { - Positions *stdcomponents.PositionComponentManager Scales *stdcomponents.ScaleComponentManager Sprites *stdcomponents.SpriteComponentManager RLTexturePros *stdcomponents.RLTextureProComponentManager @@ -36,38 +35,21 @@ type SpriteSystem struct { func (s *SpriteSystem) Init() {} func (s *SpriteSystem) Run() { - s.Sprites.EachEntity(func(entity ecs.Entity) bool { - sprite := s.Sprites.Get(entity) // - position := s.Positions.Get(entity) + s.Sprites.EachEntityParallel(func(entity ecs.Entity) bool { + sprite := s.Sprites.Get(entity) scale := s.Scales.Get(entity) + tr := s.RLTexturePros.Get(entity) - renderable := s.Renderables.Get(entity) - if renderable == nil { - renderable = s.Renderables.Create(entity, stdcomponents.SpriteRenderableType) - } - - renderOrder := s.RenderOrder.Get(entity) - if renderOrder == nil { - renderOrder = s.RenderOrder.Create(entity, stdcomponents.RenderOrder{}) + tr.Texture = sprite.Texture + tr.Frame = sprite.Frame + tr.Dest = sprite.Dest + tr.Origin = rl.Vector2{ + X: sprite.Origin.X * scale.XY.X, + Y: sprite.Origin.Y * scale.XY.Y, } + tr.Rotation = sprite.Rotation + tr.Tint = sprite.Tint - tr := s.RLTexturePros.Get(entity) - if tr == nil { - s.RLTexturePros.Create(entity, stdcomponents.RLTexturePro{ - Texture: sprite.Texture, // - Frame: sprite.Frame, // - Origin: rl.Vector2{ - X: sprite.Origin.X * scale.XY.X, - Y: sprite.Origin.Y * scale.XY.Y, - }, - Dest: rl.Rectangle{X: position.XY.X, Y: position.XY.Y, Width: sprite.Frame.Width, Height: sprite.Frame.Height}, // - Tint: sprite.Tint, - }) - } else { - tr.Dest.Width = sprite.Frame.Width - tr.Dest.Height = sprite.Frame.Height - tr.Tint = sprite.Tint - } return true }) } diff --git a/stdsystems/texture-position-smooth.go b/stdsystems/texture-position-smooth.go new file mode 100644 index 00000000..5de9e786 --- /dev/null +++ b/stdsystems/texture-position-smooth.go @@ -0,0 +1,94 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "math" + "time" +) + +func NewTexturePositionSmoothSystem() TexturePositionSmoothSystem { + return TexturePositionSmoothSystem{} +} + +type TexturePositionSmoothSystem struct { + TexturePositionSmooth *stdcomponents.TexturePositionSmoothComponentManager + Position *stdcomponents.PositionComponentManager + RLTexture *stdcomponents.RLTextureProComponentManager +} + +func (s *TexturePositionSmoothSystem) Init() { +} + +func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { + //DEBUG Temporary, TODO: remove + if rl.IsKeyPressed(rl.KeyI) { + s.TexturePositionSmooth.EachComponentParallel(func(t *stdcomponents.TexturePositionSmooth) bool { + *t = stdcomponents.TexturePositionSmoothOff + return true + }) + } + if rl.IsKeyPressed(rl.KeyO) { + s.TexturePositionSmooth.EachComponentParallel(func(t *stdcomponents.TexturePositionSmooth) bool { + *t = stdcomponents.TexturePositionSmoothLerp + return true + }) + } + if rl.IsKeyPressed(rl.KeyP) { + s.TexturePositionSmooth.EachComponentParallel(func(t *stdcomponents.TexturePositionSmooth) bool { + *t = stdcomponents.TexturePositionSmoothExpDecay + return true + }) + } + //END DEBUG + + s.TexturePositionSmooth.EachEntityParallel(func(entity ecs.Entity) bool { + position := s.Position.Get(entity) + texture := s.RLTexture.Get(entity) + smooth := s.TexturePositionSmooth.Get(entity) + if texture == nil { + return true + } + assert.Nil(position, "position is nil") + + switch *smooth { + case stdcomponents.TexturePositionSmoothLerp: + dest := vectors.Vec2{X: texture.Dest.X, Y: texture.Dest.Y} + xy := s.Lerp2D(dest, position.XY, dt) + texture.Dest.X = xy.X + texture.Dest.Y = xy.Y + case stdcomponents.TexturePositionSmoothExpDecay: + dest := vectors.Vec2{X: texture.Dest.X, Y: texture.Dest.Y} + xy := s.ExpDecay2D(dest, position.XY, 10, float64(dt)) + texture.Dest.X = xy.X + texture.Dest.Y = xy.Y + default: + } + + return true + }) +} + +func (s *TexturePositionSmoothSystem) Destroy() {} + +func (_ *TexturePositionSmoothSystem) Lerp2D(a, b vectors.Vec2, dt time.Duration) vectors.Vec2 { + return a.Add(b.Sub(a).Scale(float32(dt))) +} + +// ExpDecay2D applies an exponential decay to the position vector. +// TODO: float32 math package +func (_ *TexturePositionSmoothSystem) ExpDecay2D(a, b vectors.Vec2, decay, dt float64) vectors.Vec2 { + return vectors.Vec2{ + X: b.X + (a.X-b.X)*(float32(math.Exp(-decay*dt))), + Y: b.Y + (a.Y-b.Y)*(float32(math.Exp(-decay*dt))), + } +} diff --git a/vectors/rectangle.go b/vectors/rectangle.go new file mode 100644 index 00000000..983089f9 --- /dev/null +++ b/vectors/rectangle.go @@ -0,0 +1,19 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package vectors + +type Rectangle struct { + X, Y, Width, Height float32 +} From c2a16533e353b0d9ab2e853aaabf7ef7d6f33324 Mon Sep 17 00:00:00 2001 From: bitver Date: Thu, 10 Apr 2025 21:33:36 +0500 Subject: [PATCH 118/196] merge fix --- examples/new-api/systems/texture-circle.go | 3 ++- examples/new-api/systems/texture-rect.go | 3 ++- stdsystems/render.go | 1 + stdsystems/sprite.go | 2 +- stdsystems/texture-position-smooth.go | 8 ++++---- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go index bcd2f297..cd5c0fbe 100644 --- a/examples/new-api/systems/texture-circle.go +++ b/examples/new-api/systems/texture-circle.go @@ -12,6 +12,7 @@ import ( "gomp/examples/new-api/components" "gomp/pkg/ecs" "gomp/stdcomponents" + "math" "time" ) @@ -35,7 +36,7 @@ func (s *TextureCircleSystem) Init() { } func (s *TextureCircleSystem) Run(dt time.Duration) { - s.Circles.EachEntityParallel(func(entity ecs.Entity) bool { + s.Circles.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, i int) bool { circle := s.Circles.Get(entity) texture := s.Textures.Get(entity) assert.Nil(texture, "texture is nil; entity: %d", entity) diff --git a/examples/new-api/systems/texture-rect.go b/examples/new-api/systems/texture-rect.go index 5408a889..d7025750 100644 --- a/examples/new-api/systems/texture-rect.go +++ b/examples/new-api/systems/texture-rect.go @@ -12,6 +12,7 @@ import ( "gomp/examples/new-api/components" "gomp/pkg/ecs" "gomp/stdcomponents" + "math" "time" ) @@ -35,7 +36,7 @@ func (s *TextureRectSystem) Init() { func (s *TextureRectSystem) Run(dt time.Duration) { // Create shallow copy of texture to draw rectangles - s.TextureRect.EachEntityParallel(func(entity ecs.Entity) bool { + s.TextureRect.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, i int) bool { rect := s.TextureRect.Get(entity) texture := s.Textures.Get(entity) assert.Nil(texture, "texture is nil; entity: %d", entity) diff --git a/stdsystems/render.go b/stdsystems/render.go index f9c366f2..ac82195c 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -125,6 +125,7 @@ type RenderInjector struct { BvhTrees *stdcomponents.BvhTreeComponentManager Camera *stdcomponents.CameraComponentManager RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager + CollisionChunks *stdcomponents.CollisionChunkComponentManager } func (s *RenderSystem) InjectWorld(injector *RenderInjector) { diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index f55f8d72..f596bc4b 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -35,7 +35,7 @@ type SpriteSystem struct { func (s *SpriteSystem) Init() {} func (s *SpriteSystem) Run() { - s.Sprites.EachEntityParallel(func(entity ecs.Entity) bool { + s.Sprites.EachEntityParallel(1, func(entity ecs.Entity, i int) bool { sprite := s.Sprites.Get(entity) scale := s.Scales.Get(entity) tr := s.RLTexturePros.Get(entity) diff --git a/stdsystems/texture-position-smooth.go b/stdsystems/texture-position-smooth.go index 5de9e786..e65e739a 100644 --- a/stdsystems/texture-position-smooth.go +++ b/stdsystems/texture-position-smooth.go @@ -32,26 +32,26 @@ func (s *TexturePositionSmoothSystem) Init() { func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { //DEBUG Temporary, TODO: remove if rl.IsKeyPressed(rl.KeyI) { - s.TexturePositionSmooth.EachComponentParallel(func(t *stdcomponents.TexturePositionSmooth) bool { + s.TexturePositionSmooth.EachComponentParallel(math.MaxInt, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothOff return true }) } if rl.IsKeyPressed(rl.KeyO) { - s.TexturePositionSmooth.EachComponentParallel(func(t *stdcomponents.TexturePositionSmooth) bool { + s.TexturePositionSmooth.EachComponentParallel(math.MaxInt, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothLerp return true }) } if rl.IsKeyPressed(rl.KeyP) { - s.TexturePositionSmooth.EachComponentParallel(func(t *stdcomponents.TexturePositionSmooth) bool { + s.TexturePositionSmooth.EachComponentParallel(math.MaxInt, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothExpDecay return true }) } //END DEBUG - s.TexturePositionSmooth.EachEntityParallel(func(entity ecs.Entity) bool { + s.TexturePositionSmooth.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, i int) bool { position := s.Position.Get(entity) texture := s.RLTexture.Get(entity) smooth := s.TexturePositionSmooth.Get(entity) From 9221749c2a65f7ea1a4a8ec4ada749e2e1de56d8 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 11 Apr 2025 13:46:15 +0300 Subject: [PATCH 119/196] fix bvh-tree.go stack limit --- stdcomponents/bvh-tree.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index f6a1b163..eb34f3f6 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -180,7 +180,7 @@ func (t *BvhTree) Build() { childrenCreated bool } - stack := [32]buildTask{ + stack := [64]buildTask{ {parentIndex: 0, start: 0, end: t.Leaves.Len() - 1, childrenCreated: false}, } stackLen := 1 From 1a18f562667917a730c7f2eedb286923d45e32f5 Mon Sep 17 00:00:00 2001 From: bitver Date: Fri, 11 Apr 2025 22:46:09 +0500 Subject: [PATCH 120/196] lil optimization --- examples/new-api/systems/camera-minimap.go | 2 +- examples/new-api/systems/debug-info.go | 51 +++++---- examples/new-api/systems/render-overlay.go | 115 ++++++++++++++++++--- stdcomponents/frame-buffer-2d.go | 10 ++ stdsystems/render-2d-cameras.go | 39 ++++--- 5 files changed, 168 insertions(+), 49 deletions(-) diff --git a/examples/new-api/systems/camera-minimap.go b/examples/new-api/systems/camera-minimap.go index 776c324a..521c7199 100644 --- a/examples/new-api/systems/camera-minimap.go +++ b/examples/new-api/systems/camera-minimap.go @@ -49,7 +49,7 @@ func (s *MinimapSystem) Init() { }) s.FrameBuffer2D.Create(s.minimapCamera, stdcomponents.FrameBuffer2D{ Position: rl.Vector2{}, - Frame: rl.NewRectangle(0, 0, float32(width/6), float32(height/6)), + Frame: rl.NewRectangle(0, 0, float32(width), float32(height)), Texture: rl.LoadRenderTexture(int32(width), int32(height)), Layer: config.MinimapCameraLayer, BlendMode: rl.BlendAlpha, diff --git a/examples/new-api/systems/debug-info.go b/examples/new-api/systems/debug-info.go index 3272db5d..806c5b05 100644 --- a/examples/new-api/systems/debug-info.go +++ b/examples/new-api/systems/debug-info.go @@ -37,7 +37,9 @@ type DebugInfoSystem struct { RenderOrders *stdcomponents.RenderOrderComponentManager AABBs *stdcomponents.AABBComponentManager Circle *components.PrimitiveCircleComponentManager - camera ecs.Entity + CollisionChunks *stdcomponents.CollisionChunkComponentManager + Tints *stdcomponents.TintComponentManager + BvhTrees *stdcomponents.BvhTreeComponentManager debug bool children children @@ -56,15 +58,22 @@ func (s *DebugInfoSystem) Init() { } func (s *DebugInfoSystem) Run(dt time.Duration) bool { - if rl.IsKeyPressed(rl.KeyF2) { + if rl.IsKeyPressed(rl.KeyF6) { if !s.debug { s.BoxColliders.EachEntity(func(e ecs.Entity) bool { col := s.BoxColliders.Get(e) scale := s.Scales.Get(e) - pos := s.Positions.Get(e) - rot := s.Rotations.Get(e) + position := s.Positions.Get(e) + rotation := s.Rotations.Get(e) - s.spawnRect(col, scale, pos, rot, e) + x := position.XY.X + y := position.XY.Y + width := col.WH.X * scale.XY.X + height := col.WH.Y * scale.XY.Y + s.spawnRect(x, y, width, height, rl.Vector2{ + X: col.Offset.X * scale.XY.X, + Y: col.Offset.Y * scale.XY.Y, + }, float32(rotation.Degrees()), rl.DarkGreen, e) return true }) s.CircleColliders.EachEntity(func(e ecs.Entity) bool { @@ -122,7 +131,14 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { } else { //TODO: defer spawning to non parallel function - s.spawnRect(col, scale, parentPosition, rotation, e) + x := parentPosition.XY.X + y := parentPosition.XY.Y + width := col.WH.X * scale.XY.X + height := col.WH.Y * scale.XY.Y + s.spawnRect(x, y, width, height, rl.Vector2{ + X: col.Offset.X * scale.XY.X, + Y: col.Offset.Y * scale.XY.Y, + }, float32(rotation.Degrees()), rl.DarkGreen, e) } return true @@ -183,22 +199,19 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { return false } -func (s *DebugInfoSystem) spawnRect(col *stdcomponents.BoxCollider, scale *stdcomponents.Scale, pos *stdcomponents.Position, rot *stdcomponents.Rotation, e ecs.Entity) { - if _, ok := s.children[e]; !ok { +func (s *DebugInfoSystem) spawnRect(x float32, y float32, width float32, height float32, origin rl.Vector2, rotation float32, tint color.RGBA, parent ecs.Entity) { + if _, ok := s.children[parent]; !ok { childEntity := s.EntityManager.Create() s.TextureRect.Create(childEntity, components.TextureRect{ Dest: rl.Rectangle{ - X: pos.XY.X, - Y: pos.XY.Y, - Width: col.WH.X * scale.XY.X, - Height: col.WH.Y * scale.XY.Y, + X: x, + Y: y, + Width: width, + Height: height, }, - Origin: rl.Vector2{ - X: col.Offset.X * scale.XY.X, - Y: col.Offset.Y * scale.XY.Y, - }, - Rotation: float32(rot.Degrees()), - Color: rl.DarkGreen, + Origin: origin, + Rotation: rotation, + Color: tint, }) s.AABBs.Create(childEntity, stdcomponents.AABB{}) s.Texture.Create(childEntity, stdcomponents.RLTexturePro{}) @@ -209,7 +222,7 @@ func (s *DebugInfoSystem) spawnRect(col *stdcomponents.BoxCollider, scale *stdco s.RenderOrders.Create(childEntity, stdcomponents.RenderOrder{ CalculatedZ: math.MaxInt, }) - s.children[e] = childType{ + s.children[parent] = childType{ id: childEntity, isAlive: false, } diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index 78ce865b..b0a151cb 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -17,10 +17,13 @@ package systems import ( "fmt" rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" "gomp/examples/new-api/components" "gomp/examples/new-api/config" "gomp/pkg/ecs" "gomp/stdcomponents" + "gomp/vectors" + "image/color" "time" ) @@ -29,12 +32,18 @@ func NewRenderOverlaySystem() RenderOverlaySystem { } type RenderOverlaySystem struct { - EntityManager *ecs.EntityManager - SceneManager *components.AsteroidSceneManagerComponentManager - Cameras *stdcomponents.CameraComponentManager - FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager - monitorWidth int - monitorHeight int + EntityManager *ecs.EntityManager + SceneManager *components.AsteroidSceneManagerComponentManager + Cameras *stdcomponents.CameraComponentManager + FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager + CollisionChunks *stdcomponents.CollisionChunkComponentManager + Tints *stdcomponents.TintComponentManager + BvhTrees *stdcomponents.BvhTreeComponentManager + Positions *stdcomponents.PositionComponentManager + monitorWidth int + monitorHeight int + debugLvl int + debug bool } func (s *RenderOverlaySystem) Init() { @@ -43,15 +52,87 @@ func (s *RenderOverlaySystem) Init() { } func (s *RenderOverlaySystem) Run(dt time.Duration) bool { - - s.FrameBuffer2D.EachComponent(func(c *stdcomponents.FrameBuffer2D) bool { - switch c.Layer { + if rl.IsKeyPressed(rl.KeyF6) { + if !s.debug { + s.debug = true + } else { + s.debug = false + } + } + if rl.IsKeyPressed(rl.KeyF7) { + s.debugLvl-- + if s.debugLvl < 0 { + s.debugLvl = 63 + } + } + if rl.IsKeyPressed(rl.KeyF8) { + s.debugLvl++ + if s.debugLvl > 63 { + s.debugLvl = 0 + } + } + s.FrameBuffer2D.EachEntity(func(entity ecs.Entity) bool { + frame := s.FrameBuffer2D.Get(entity) + camera := s.Cameras.Get(entity) + switch frame.Layer { case config.MainCameraLayer: - rl.BeginTextureMode(c.Texture) - // Print the current FPS + rl.BeginTextureMode(frame.Texture) + + // BVH tree + if s.debug { + rl.BeginMode2D(camera.Camera2D) + cameraRect := camera.Rect() + s.CollisionChunks.EachEntity(func(e ecs.Entity) bool { + chunk := s.CollisionChunks.Get(e) + assert.NotNil(chunk) + + if chunk.Layer != stdcomponents.CollisionLayer(s.debugLvl) { + return true + } + + tint := s.Tints.Get(e) + assert.NotNil(tint) + + position := s.Positions.Get(e) + assert.NotNil(position) + + tree := s.BvhTrees.Get(e) + assert.NotNil(tree) + + tree.AabbNodes.EachData(func(a *stdcomponents.AABB) bool { + // Simple AABB culling + if s.intersects(cameraRect, a.Rect()) { + rl.DrawRectangle(int32(a.Min.X), int32(a.Min.Y), int32(a.Max.X-a.Min.X), int32(a.Max.Y-a.Min.Y), *tint) + } + return true + }) + + clr := color.RGBA{ + R: tint.R, + G: tint.G, + B: tint.B, + A: 255, + } + + // Simple AABB culling + if s.intersects(cameraRect, vectors.Rectangle{ + X: position.XY.X, + Y: position.XY.Y, + Width: chunk.Size, + Height: chunk.Size, + }) { + rl.DrawRectangleLines(int32(position.XY.X), int32(position.XY.Y), int32(chunk.Size), int32(chunk.Size), clr) + } + return true + }) + rl.EndMode2D() + } + + // Print stats rl.DrawRectangleRec(rl.Rectangle{Height: 100, Width: 200}, rl.Black) rl.DrawFPS(10, 10) rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) + // Game over s.SceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), 10, 50, 20, rl.RayWhite) @@ -67,9 +148,10 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { return false }) rl.EndTextureMode() + case config.MinimapCameraLayer: - rl.BeginTextureMode(c.Texture) - rl.DrawRectangleLines(1, 1, c.Texture.Texture.Width-1, c.Texture.Texture.Height-1, rl.Green) + rl.BeginTextureMode(frame.Texture) + rl.DrawRectangleLines(1, 1, frame.Texture.Texture.Width-1, frame.Texture.Texture.Height-1, rl.Green) rl.EndTextureMode() } return true @@ -78,4 +160,11 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { return true } +func (s *RenderOverlaySystem) intersects(rect1, rect2 vectors.Rectangle) bool { + return rect1.X < rect2.X+rect2.Width && + rect1.X+rect1.Width > rect2.X && + rect1.Y < rect2.Y+rect2.Height && + rect1.Y+rect1.Height > rect2.Y +} + func (s *RenderOverlaySystem) Destroy() {} diff --git a/stdcomponents/frame-buffer-2d.go b/stdcomponents/frame-buffer-2d.go index aa87e53a..a7c053c1 100644 --- a/stdcomponents/frame-buffer-2d.go +++ b/stdcomponents/frame-buffer-2d.go @@ -17,6 +17,7 @@ package stdcomponents import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" + "gomp/vectors" "image/color" ) @@ -31,6 +32,15 @@ type FrameBuffer2D struct { Dst rl.Rectangle } +func (d FrameBuffer2D) FrameRect() vectors.Rectangle { + return vectors.Rectangle{ + X: d.Frame.X, + Y: d.Frame.Y, + Width: d.Frame.Width, + Height: d.Frame.Height, + } +} + type FrameBuffer2DComponentManager = ecs.ComponentManager[FrameBuffer2D] func NewFrameBuffer2DComponentManager() FrameBuffer2DComponentManager { diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index fc177647..dba63123 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -45,6 +45,7 @@ func (s *Render2DCamerasSystem) Init() { func (s *Render2DCamerasSystem) Run(dt time.Duration) { s.Cameras.EachEntity(func(entity ecs.Entity) bool { camera := s.Cameras.Get(entity) + cameraRect := camera.Rect() renderTexture := s.RenderTexture2D.Get(entity) // Collect and sort render objects @@ -53,14 +54,29 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { t := s.Textures.Get(entity) o := s.RenderOrders.Get(entity) aabb := s.AABBs.Get(entity) - s.renderObjects = append(s.renderObjects, renderObject{ - texture: t, - renderable: r, - order: o.CalculatedZ, - aabb: aabb, - }) + + switch camera.Culling { + case stdcomponents.Culling2DFullscreenBB: + if aabb != nil && intersects(cameraRect, aabb.Rect()) { + s.renderObjects = append(s.renderObjects, renderObject{ + texture: t, + renderable: r, + order: o.CalculatedZ, + aabb: aabb, + }) + } + default: + s.renderObjects = append(s.renderObjects, renderObject{ + texture: t, + renderable: r, + order: o.CalculatedZ, + aabb: aabb, + }) + } + return true }) + slices.SortFunc(s.renderObjects, func(a, b renderObject) int { return cmp.Compare(a.order, b.order) }) @@ -70,19 +86,10 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { rl.BeginMode2D(camera.Camera2D) rl.ClearBackground(camera.BGColor) - cameraRect := camera.Rect() for _, obj := range s.renderObjects { if camera.Layer&obj.renderable.CameraMask != 0 { assert.Nil(obj.texture, "EntityTexturePro is nil") - switch camera.Culling { - case stdcomponents.Culling2DFullscreenBB: - if obj.aabb != nil && intersects(cameraRect, obj.aabb.Rect()) { - rl.DrawTexturePro(*obj.texture.Texture, obj.texture.Frame, obj.texture.Dest, obj.texture.Origin, obj.texture.Rotation, obj.texture.Tint) - } - default: - rl.DrawTexturePro(*obj.texture.Texture, obj.texture.Frame, obj.texture.Dest, obj.texture.Origin, obj.texture.Rotation, obj.texture.Tint) - } - + rl.DrawTexturePro(*obj.texture.Texture, obj.texture.Frame, obj.texture.Dest, obj.texture.Origin, obj.texture.Rotation, obj.texture.Tint) } } From fd6b4c62605cc615657fec18125d07c3478ee26d Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 12:34:20 +0500 Subject: [PATCH 121/196] pre-merge --- examples/new-api/entities/wall.go | 2 +- examples/new-api/game.go | 24 +------ examples/new-api/systems/camera-minimap.go | 24 ++++++- .../new-api/systems/position-to-sprite.go | 23 +++--- examples/new-api/systems/render-overlay.go | 72 ++++++++++++++----- stdsystems/render.go | 72 ++----------------- 6 files changed, 96 insertions(+), 121 deletions(-) diff --git a/examples/new-api/entities/wall.go b/examples/new-api/entities/wall.go index 4762c983..d4de7593 100644 --- a/examples/new-api/entities/wall.go +++ b/examples/new-api/entities/wall.go @@ -105,7 +105,7 @@ func CreateWall( props.WallTags.Create(entity, components.Wall{}) props.Renderables.Create(entity, stdcomponents.Renderable{ Type: stdcomponents.SpriteRenderableType, - CameraMask: config.MainCameraLayer, + CameraMask: config.MainCameraLayer | config.MinimapCameraLayer, }) props.RenderOrders.Create(entity, stdcomponents.RenderOrder{}) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 5cfbc45a..904ef1fa 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -175,28 +175,8 @@ func (g *Game) SetActiveScene(id gomp.SceneId) { g.renderSystem.InjectWorld( &stdsystems.RenderInjector{ - EntityManager: &world.Entities, - RlTexturePros: &components.RLTexturePro, - Positions: &components.Position, - Rotations: &components.Rotation, - Scales: &components.Scale, - AnimationPlayers: &components.AnimationPlayer, - Tints: &components.Tint, - Flips: &components.Flip, - Renderables: &components.Renderable, - AnimationStates: &components.AnimationState, - Sprites: &components.Sprite, - SpriteMatrixes: &components.SpriteMatrix, - RenderOrders: &components.RenderOrder, - BoxColliders: &components.ColliderBox, - CircleColliders: &components.ColliderCircle, - AABBs: &components.AABB, - Collisions: &components.Collision, - ColliderSleepStateComponentManager: &components.ColliderSleepState, - BvhTrees: &components.BvhTree, - Camera: &components.Cameras, - RenderTexture2D: &components.FrameBuffer2D, - CollisionChunks: &components.CollisionChunk, + EntityManager: &world.Entities, + FrameBuffer2D: &components.FrameBuffer2D, }) } diff --git a/examples/new-api/systems/camera-minimap.go b/examples/new-api/systems/camera-minimap.go index 521c7199..e58ea7b3 100644 --- a/examples/new-api/systems/camera-minimap.go +++ b/examples/new-api/systems/camera-minimap.go @@ -25,7 +25,10 @@ type MinimapSystem struct { FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager Player *components.PlayerTagComponentManager - minimapCamera ecs.Entity + minimapCamera ecs.Entity + frameBufferComponent stdcomponents.FrameBuffer2D + cameraComponent stdcomponents.Camera + disabled bool } func (s *MinimapSystem) Init() { @@ -60,9 +63,26 @@ func (s *MinimapSystem) Init() { } func (s *MinimapSystem) Run(dt time.Duration) bool { + c := s.Cameras.Get(s.minimapCamera) + + if rl.IsKeyPressed(rl.KeyM) { + if s.disabled { + s.disabled = false + c = s.Cameras.Create(s.minimapCamera, s.cameraComponent) + s.FrameBuffer2D.Create(s.minimapCamera, s.frameBufferComponent) + } else { + s.disabled = true + s.cameraComponent = *c + s.frameBufferComponent = *s.FrameBuffer2D.Get(s.minimapCamera) + s.Cameras.Remove(s.minimapCamera) + s.FrameBuffer2D.Remove(s.minimapCamera) + } + } + if s.disabled { + return false + } s.Player.EachEntity(func(entity ecs.Entity) bool { playerPosition := s.Position.Get(entity) - c := s.Cameras.Get(s.minimapCamera) c.Camera2D.Target.X = playerPosition.XY.X c.Camera2D.Target.Y = playerPosition.XY.Y rotation := s.Rotation.Get(entity) diff --git a/examples/new-api/systems/position-to-sprite.go b/examples/new-api/systems/position-to-sprite.go index b414fee3..05066daa 100644 --- a/examples/new-api/systems/position-to-sprite.go +++ b/examples/new-api/systems/position-to-sprite.go @@ -9,6 +9,7 @@ package systems import ( "gomp/pkg/ecs" "gomp/stdcomponents" + "math" "time" ) @@ -30,28 +31,26 @@ func (s *PositionToSpriteSystem) Init() { } func (s *PositionToSpriteSystem) Run(dt time.Duration) { - s.Sprite.EachEntity(func(entity ecs.Entity) bool { + s.Sprite.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, _ int) bool { position := s.Position.Get(entity) sprite := s.Sprite.Get(entity) + rotation := s.Rotation.Get(entity) + sprite.Dest.X = position.XY.X sprite.Dest.Y = position.XY.Y - //TODO: refactor - rotation := s.Rotation.Get(entity) - if rotation != nil { - sprite.Rotation = float32(rotation.Degrees()) - } + sprite.Rotation = float32(rotation.Degrees()) + return true }) - s.SpriteMatrix.EachEntity(func(entity ecs.Entity) bool { + s.SpriteMatrix.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, _ int) bool { position := s.Position.Get(entity) sprite := s.SpriteMatrix.Get(entity) + rotation := s.Rotation.Get(entity) + sprite.Dest.X = position.XY.X sprite.Dest.Y = position.XY.Y - //TODO: refactor - rotation := s.Rotation.Get(entity) - if rotation != nil { - sprite.Rotation = rotation.Angle - } + sprite.Rotation = rotation.Angle + return true }) } diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index b0a151cb..2afb7922 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -32,18 +32,22 @@ func NewRenderOverlaySystem() RenderOverlaySystem { } type RenderOverlaySystem struct { - EntityManager *ecs.EntityManager - SceneManager *components.AsteroidSceneManagerComponentManager - Cameras *stdcomponents.CameraComponentManager - FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager - CollisionChunks *stdcomponents.CollisionChunkComponentManager - Tints *stdcomponents.TintComponentManager - BvhTrees *stdcomponents.BvhTreeComponentManager - Positions *stdcomponents.PositionComponentManager - monitorWidth int - monitorHeight int - debugLvl int - debug bool + EntityManager *ecs.EntityManager + SceneManager *components.AsteroidSceneManagerComponentManager + Cameras *stdcomponents.CameraComponentManager + FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager + CollisionChunks *stdcomponents.CollisionChunkComponentManager + Tints *stdcomponents.TintComponentManager + BvhTrees *stdcomponents.BvhTreeComponentManager + Positions *stdcomponents.PositionComponentManager + AABBs *stdcomponents.AABBComponentManager + Collisions *stdcomponents.CollisionComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + Textures *stdcomponents.RLTextureProComponentManager + monitorWidth int + monitorHeight int + debugLvl int + debug bool } func (s *RenderOverlaySystem) Init() { @@ -71,6 +75,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { s.debugLvl = 0 } } + s.FrameBuffer2D.EachEntity(func(entity ecs.Entity) bool { frame := s.FrameBuffer2D.Get(entity) camera := s.Cameras.Get(entity) @@ -78,7 +83,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { case config.MainCameraLayer: rl.BeginTextureMode(frame.Texture) - // BVH tree + // Debug mode: BVH tree and dots if s.debug { rl.BeginMode2D(camera.Camera2D) cameraRect := camera.Rect() @@ -125,18 +130,49 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } return true }) + s.AABBs.EachEntity(func(e ecs.Entity) bool { + aabb := s.AABBs.Get(e) + clr := rl.Green + isSleeping := s.ColliderSleepStateComponentManager.Get(e) + if isSleeping != nil { + clr = rl.Blue + } + if s.intersects(cameraRect, aabb.Rect()) { + rl.DrawRectangleLinesEx(rl.Rectangle{ + X: aabb.Min.X, + Y: aabb.Min.Y, + Width: aabb.Max.X - aabb.Min.X, + Height: aabb.Max.Y - aabb.Min.Y, + }, 1, clr) + } + return true + }) + s.Collisions.EachEntity(func(entity ecs.Entity) bool { + pos := s.Positions.Get(entity) + rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) + return true + }) + s.Textures.EachComponent(func(r *stdcomponents.RLTexturePro) bool { + rl.DrawRectanglePro(rl.Rectangle{ + X: r.Dest.X - 2, + Y: r.Dest.Y - 2, + Width: 4, + Height: 4, + }, rl.Vector2{}, r.Rotation, rl.Red) + return true + }) rl.EndMode2D() } // Print stats - rl.DrawRectangleRec(rl.Rectangle{Height: 100, Width: 200}, rl.Black) + rl.DrawRectangleRec(rl.Rectangle{Height: 120, Width: 200}, rl.Black) rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) - + rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 70, 20, rl.RayWhite) + rl.DrawText(fmt.Sprintf("%d debugLvl", s.debugLvl), 10, 90, 20, rl.RayWhite) // Game over s.SceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { - rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), 10, 50, 20, rl.RayWhite) - rl.DrawText(fmt.Sprintf("Score: %d", a.PlayerScore), 10, 70, 20, rl.RayWhite) + rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), 10, 30, 20, rl.RayWhite) + rl.DrawText(fmt.Sprintf("Score: %d", a.PlayerScore), 10, 50, 20, rl.RayWhite) if a.PlayerHp <= 0 { text := "Game Over" textSize := rl.MeasureTextEx(rl.GetFontDefault(), text, 96, 0) diff --git a/stdsystems/render.go b/stdsystems/render.go index ac82195c..23d1edcc 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -27,41 +27,20 @@ func NewRenderSystem() RenderSystem { } type RenderSystem struct { - EntityManager *ecs.EntityManager - RlTexturePros *stdcomponents.RLTextureProComponentManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - AnimationPlayers *stdcomponents.AnimationPlayerComponentManager - Tints *stdcomponents.TintComponentManager - Flips *stdcomponents.FlipComponentManager - Renderables *stdcomponents.RenderableComponentManager - AnimationStates *stdcomponents.AnimationStateComponentManager - Sprites *stdcomponents.SpriteComponentManager - SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - CircleColliders *stdcomponents.CircleColliderComponentManager - AABBs *stdcomponents.AABBComponentManager - Collisions *stdcomponents.CollisionComponentManager - ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager - BvhTrees *stdcomponents.BvhTreeComponentManager - CamerasManager *stdcomponents.CameraComponentManager - FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager + EntityManager *ecs.EntityManager + FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager renderTextures []rl.RenderTexture2D frames []stdcomponents.FrameBuffer2D monitorWidth int monitorHeight int - - debug bool } func (s *RenderSystem) Init() { s.monitorWidth = rl.GetScreenWidth() s.monitorHeight = rl.GetScreenHeight() - s.renderTextures = make([]rl.RenderTexture2D, 0, s.CamerasManager.Len()) + s.renderTextures = make([]rl.RenderTexture2D, 0, s.FrameBuffer2D.Len()) s.frames = make([]stdcomponents.FrameBuffer2D, 0, s.FrameBuffer2D.Len()) } @@ -104,50 +83,11 @@ func (s *RenderSystem) Destroy() { } type RenderInjector struct { - EntityManager *ecs.EntityManager - RlTexturePros *stdcomponents.RLTextureProComponentManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - AnimationPlayers *stdcomponents.AnimationPlayerComponentManager - Tints *stdcomponents.TintComponentManager - Flips *stdcomponents.FlipComponentManager - Renderables *stdcomponents.RenderableComponentManager - AnimationStates *stdcomponents.AnimationStateComponentManager - Sprites *stdcomponents.SpriteComponentManager - SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - CircleColliders *stdcomponents.CircleColliderComponentManager - AABBs *stdcomponents.AABBComponentManager - Collisions *stdcomponents.CollisionComponentManager - ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager - BvhTrees *stdcomponents.BvhTreeComponentManager - Camera *stdcomponents.CameraComponentManager - RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager - CollisionChunks *stdcomponents.CollisionChunkComponentManager + EntityManager *ecs.EntityManager + FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager } func (s *RenderSystem) InjectWorld(injector *RenderInjector) { s.EntityManager = injector.EntityManager - s.RlTexturePros = injector.RlTexturePros - s.Positions = injector.Positions - s.Rotations = injector.Rotations - s.Scales = injector.Scales - s.AnimationPlayers = injector.AnimationPlayers - s.Tints = injector.Tints - s.Flips = injector.Flips - s.Renderables = injector.Renderables - s.AnimationStates = injector.AnimationStates - s.Sprites = injector.Sprites - s.SpriteMatrixes = injector.SpriteMatrixes - s.RenderOrders = injector.RenderOrders - s.BoxColliders = injector.BoxColliders - s.CircleColliders = injector.CircleColliders - s.AABBs = injector.AABBs - s.Collisions = injector.Collisions - s.ColliderSleepStateComponentManager = injector.ColliderSleepStateComponentManager - s.BvhTrees = injector.BvhTrees - s.CamerasManager = injector.Camera - s.FrameBuffer2D = injector.RenderTexture2D + s.FrameBuffer2D = injector.FrameBuffer2D } From 35877164c9460b4b4d6e9d4153a65a6e09972bcf Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 12:42:44 +0500 Subject: [PATCH 122/196] pre-merge --- stdsystems/render-2d-cameras.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index dba63123..5539b9e2 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -55,9 +55,18 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { o := s.RenderOrders.Get(entity) aabb := s.AABBs.Get(entity) - switch camera.Culling { - case stdcomponents.Culling2DFullscreenBB: - if aabb != nil && intersects(cameraRect, aabb.Rect()) { + if t != nil { + switch camera.Culling { + case stdcomponents.Culling2DFullscreenBB: + if aabb != nil && intersects(cameraRect, aabb.Rect()) { + s.renderObjects = append(s.renderObjects, renderObject{ + texture: t, + renderable: r, + order: o.CalculatedZ, + aabb: aabb, + }) + } + default: s.renderObjects = append(s.renderObjects, renderObject{ texture: t, renderable: r, @@ -65,13 +74,6 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { aabb: aabb, }) } - default: - s.renderObjects = append(s.renderObjects, renderObject{ - texture: t, - renderable: r, - order: o.CalculatedZ, - aabb: aabb, - }) } return true From 054973b2b205d2622a94f66346d46a6ba9117e20 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 13:49:42 +0500 Subject: [PATCH 123/196] random crash fix --- stdsystems/render-2d-cameras.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index 5539b9e2..f97a32a7 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -55,7 +55,8 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { o := s.RenderOrders.Get(entity) aabb := s.AABBs.Get(entity) - if t != nil { + //TODO: rework this with future new assets manager + if t != nil && t.Texture != nil { switch camera.Culling { case stdcomponents.Culling2DFullscreenBB: if aabb != nil && intersects(cameraRect, aabb.Rect()) { From b8a760cb80e11db58da50f4ef70ea33ee847d725 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 20:31:44 +0500 Subject: [PATCH 124/196] return non-perfomant systems --- examples/new-api/entities/asteroid.go | 10 - examples/new-api/entities/bullet.go | 12 -- examples/new-api/entities/satellite.go | 5 - examples/new-api/entities/spaceship.go | 25 +-- examples/new-api/entities/wall.go | 10 - examples/new-api/game.go | 6 +- examples/new-api/instances/component-list.go | 2 + examples/new-api/instances/system-list.go | 4 +- examples/new-api/scenes/assterodd-scene.go | 5 - examples/new-api/systems/asterodd.go | 10 - examples/new-api/systems/camera-main.go | 2 +- .../new-api/systems/position-to-sprite.go | 59 ------ examples/new-api/systems/render-overlay.go | 29 ++- examples/new-api/systems/space-spawner.go | 2 - examples/new-api/systems/spaceship-intents.go | 3 - stdcomponents/ids.go | 1 + stdcomponents/render-visible.go | 25 +++ stdcomponents/sprite.go | 10 +- stdsystems/collision-detection.go | 2 +- stdsystems/culling.go | 79 ++++++++ stdsystems/render-2d-cameras.go | 184 ++++++++++++++---- stdsystems/render.go | 1 + stdsystems/sprite-matrix.go | 9 +- stdsystems/sprite.go | 36 ++-- 24 files changed, 327 insertions(+), 204 deletions(-) delete mode 100644 examples/new-api/systems/position-to-sprite.go create mode 100644 stdcomponents/render-visible.go create mode 100644 stdsystems/culling.go diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index 36a7a53b..b897deb1 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -38,8 +38,6 @@ type CreateAsteroidManagers struct { Hp *components.HpComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager Renderables *stdcomponents.RenderableComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - Textures *stdcomponents.RLTextureProComponentManager } func CreateAsteroid( @@ -88,12 +86,6 @@ func CreateAsteroid( X: 32, Y: 32, }, - Dest: rl.Rectangle{ - X: posX, - Y: posY, - Width: 64 * scaleFactor, - Height: 64 * scaleFactor, - }, Tint: color.RGBA{ R: 255, G: 255, @@ -101,8 +93,6 @@ func CreateAsteroid( A: 255, }, }) - props.Textures.Create(e, stdcomponents.RLTexturePro{}) - props.RenderOrders.Create(e, stdcomponents.RenderOrder{}) props.AsteroidTags.Create(e, components.AsteroidTag{}) hp := int32(3 + rand.Intn(6)) props.Hp.Create(e, components.Hp{ diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 028759b7..c86b4241 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -36,10 +36,7 @@ type CreateBulletManagers struct { Sprites *stdcomponents.SpriteComponentManager BulletTags *components.BulletTagComponentManager Hps *components.HpComponentManager - Smooth *stdcomponents.TexturePositionSmoothComponentManager Renderables *stdcomponents.RenderableComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - Textures *stdcomponents.RLTextureProComponentManager } func CreateBullet( @@ -84,12 +81,6 @@ func CreateBullet( Width: 64, Height: 64, }, - Dest: rl.Rectangle{ - X: posX, - Y: posY, - Width: 64, - Height: 64, - }, Origin: rl.Vector2{ X: 32, Y: 32, @@ -102,7 +93,6 @@ func CreateBullet( }, } props.Sprites.Create(entity, te) - props.Textures.Create(entity, stdcomponents.RLTexturePro{}) props.BulletTags.Create(entity, components.BulletTag{}) props.Hps.Create(entity, components.Hp{ Hp: 1, @@ -112,12 +102,10 @@ func CreateBullet( IsStatic: false, Mass: 1, }) - props.Smooth.Create(entity, stdcomponents.TexturePositionSmoothLerp) props.Renderables.Create(entity, stdcomponents.Renderable{ Type: stdcomponents.SpriteRenderableType, CameraMask: config.MainCameraLayer, }) - props.RenderOrders.Create(entity, stdcomponents.RenderOrder{}) return entity } diff --git a/examples/new-api/entities/satellite.go b/examples/new-api/entities/satellite.go index 9a66126f..6953ad32 100644 --- a/examples/new-api/entities/satellite.go +++ b/examples/new-api/entities/satellite.go @@ -34,8 +34,6 @@ type CreateSatelliteManagers struct { RigidBodies *stdcomponents.RigidBodyComponentManager Velocities *stdcomponents.VelocityComponentManager Renderables *stdcomponents.RenderableComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - Textures *stdcomponents.RLTextureProComponentManager } func CreateSatellite( @@ -68,11 +66,9 @@ func CreateSatellite( props.Sprites.Create(entity, stdcomponents.Sprite{ Texture: assets.Textures.Get("satellite_B.png"), Origin: rl.Vector2{X: 32, Y: 40}, - Dest: rl.Rectangle{0, 0, 64, 64}, Frame: rl.Rectangle{0, 0, 64, 64}, Tint: color.RGBA{255, 255, 255, 255}, }) - props.Textures.Create(entity, stdcomponents.RLTexturePro{}) props.BoxColliders.Create(entity, stdcomponents.BoxCollider{ WH: vectors.Vec2{ @@ -94,7 +90,6 @@ func CreateSatellite( Type: stdcomponents.SpriteRenderableType, CameraMask: config.MainCameraLayer | config.MinimapCameraLayer, }) - props.RenderOrders.Create(entity, stdcomponents.RenderOrder{}) return entity } diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go index 231e1375..d7c93fae 100644 --- a/examples/new-api/entities/spaceship.go +++ b/examples/new-api/entities/spaceship.go @@ -27,24 +27,21 @@ import ( ) type CreateSpaceShipManagers struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - Velocities *stdcomponents.VelocityComponentManager - Sprites *stdcomponents.SpriteComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - RigidBodies *stdcomponents.RigidBodyComponentManager - SmoothPositions *stdcomponents.TexturePositionSmoothComponentManager - Renderables *stdcomponents.RenderableComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Velocities *stdcomponents.VelocityComponentManager + Sprites *stdcomponents.SpriteComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager + Renderables *stdcomponents.RenderableComponentManager PlayerTags *components.PlayerTagComponentManager Hps *components.HpComponentManager Weapons *components.WeaponComponentManager SpaceshipIntents *components.SpaceshipIntentComponentManager SoundEffects *components.SoundEffectsComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - Textures *stdcomponents.RLTextureProComponentManager } func createMask(layers ...stdcomponents.CollisionLayer) (mask stdcomponents.CollisionMask) { @@ -85,11 +82,9 @@ func CreateSpaceShip( props.Sprites.Create(entity, stdcomponents.Sprite{ Texture: assets.Textures.Get("ship_E.png"), Origin: rl.Vector2{X: 32, Y: 40}, - Dest: rl.Rectangle{0, 0, 64, 64}, Frame: rl.Rectangle{0, 0, 64, 64}, Tint: color.RGBA{255, 255, 255, 255}, }) - props.Textures.Create(entity, stdcomponents.RLTexturePro{}) props.BoxColliders.Create(entity, stdcomponents.BoxCollider{ WH: vectors.Vec2{ @@ -132,12 +127,10 @@ func CreateSpaceShip( Pan: 0.5, }) - props.SmoothPositions.Create(entity, stdcomponents.TexturePositionSmoothExpDecay) props.Renderables.Create(entity, stdcomponents.Renderable{ Type: stdcomponents.SpriteRenderableType, CameraMask: config.MainCameraLayer | config.MinimapCameraLayer, }) - props.RenderOrders.Create(entity, stdcomponents.RenderOrder{}) return entity } diff --git a/examples/new-api/entities/wall.go b/examples/new-api/entities/wall.go index d4de7593..400143f4 100644 --- a/examples/new-api/entities/wall.go +++ b/examples/new-api/entities/wall.go @@ -36,8 +36,6 @@ type CreateWallManagers struct { RigidBodies *stdcomponents.RigidBodyComponentManager Renderables *stdcomponents.RenderableComponentManager WallTags *components.WallTagComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - Textures *stdcomponents.RLTextureProComponentManager } func CreateWall( @@ -84,12 +82,6 @@ func CreateWall( Width: width, Height: height, }, - Dest: rl.Rectangle{ - X: posX, - Y: posY, - Width: width, - Height: height, - }, Origin: rl.Vector2{ X: 0, Y: 0, @@ -101,13 +93,11 @@ func CreateWall( A: 255, }, }) - props.Textures.Create(entity, stdcomponents.RLTexturePro{}) props.WallTags.Create(entity, components.Wall{}) props.Renderables.Create(entity, stdcomponents.Renderable{ Type: stdcomponents.SpriteRenderableType, CameraMask: config.MainCameraLayer | config.MinimapCameraLayer, }) - props.RenderOrders.Create(entity, stdcomponents.RenderOrder{}) return entity } diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 904ef1fa..2d152e6f 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -116,7 +116,6 @@ func (g *Game) Render(dt time.Duration) { systems.AnimationSpriteMatrix.Run() systems.AnimationPlayer.Run() - systems.PositionToSprite.Run(dt) systems.SpriteMatrix.Run() systems.Sprite.Run() systems.Debug.Run() @@ -125,6 +124,10 @@ func (g *Game) Render(dt time.Duration) { systems.Audio.Run(dt) systems.SpatialAudio.Run(dt) + systems.Culling.Run(dt) + // Render all renderables with cameras + systems.RenderCameras.Run(dt) + scene.Render(dt) g.renderSystem.Run(dt) @@ -152,6 +155,7 @@ func (g *Game) Destroy() { systems.Audio.Destroy() systems.SpatialAudio.Destroy() systems.TexturePositionSmooth.Destroy() + systems.Culling.Destroy() world.Destroy() } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index ef6d0a75..cfb64bb5 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -63,6 +63,7 @@ type ComponentList struct { SoundEffects components.SoundEffectsComponentManager TextureRect components.TextureRectComponentManager PrimitiveCircle components.PrimitiveCircleComponentManager + RenderVisible stdcomponents.RenderVisibleComponentManager } func NewComponentList() ComponentList { @@ -80,6 +81,7 @@ func NewComponentList() ComponentList { RLTexturePro: stdcomponents.NewRlTextureProComponentManager(), Network: stdcomponents.NewNetworkComponentManager(), Renderable: stdcomponents.NewRenderableComponentManager(), + RenderVisible: stdcomponents.NewRenderVisibleComponentManager(), YSort: stdcomponents.NewYSortComponentManager(), RenderOrder: stdcomponents.NewRenderOrderComponentManager(), GenericCollider: stdcomponents.NewGenericColliderComponentManager(), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index e5348811..d807d5bd 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -42,6 +42,7 @@ func NewSystemList() SystemList { CollisionResolution: stdsystems.NewCollisionResolutionSystem(), TexturePositionSmooth: stdsystems.NewTexturePositionSmoothSystem(), RenderCameras: stdsystems.NewRender2DCamerasSystem(), + Culling: stdsystems.NewCullingSystem(), Player: systems.NewPlayerSystem(), RenderBogdan: systems.NewRenderBogdanSystem(), Audio: systems.NewAudioSystem(), @@ -53,7 +54,6 @@ func NewSystemList() SystemList { SpaceSpawner: systems.NewSpaceSpawnerSystem(), Hp: systems.NewHpSystem(), MainCamera: systems.NewMainCameraSystem(), - PositionToSprite: systems.NewPositionToSpriteSystem(), TextureRect: systems.NewTextureRectSystem(), TextureCircle: systems.NewTextureCircleSystem(), Minimap: systems.NewMinimapSystem(), @@ -84,6 +84,7 @@ type SystemList struct { CollisionResolution stdsystems.CollisionResolutionSystem TexturePositionSmooth stdsystems.TexturePositionSmoothSystem RenderCameras stdsystems.Render2DCamerasSystem + Culling stdsystems.CullingSystem RenderBogdan systems.RenderBogdanSystem Player systems.PlayerSystem Audio systems.AudioSystem @@ -95,7 +96,6 @@ type SystemList struct { SpaceSpawner systems.SpaceSpawnerSystem Hp systems.HpSystem MainCamera systems.MainCameraSystem - PositionToSprite systems.PositionToSpriteSystem Minimap systems.MinimapSystem DebugInfo systems.DebugInfoSystem TextureRect systems.TextureRectSystem diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index d33e65c8..93f8c635 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -75,7 +75,6 @@ func (s *AssteroddScene) Init(world ecs.AnyWorld) { s.World.Systems.SpaceshipIntents.Init() s.World.Systems.MainCamera.Init() s.World.Systems.Minimap.Init() - s.World.Systems.PositionToSprite.Init() s.World.Systems.TexturePositionSmooth.Init() s.World.Systems.TextureRect.Init() s.World.Systems.TextureCircle.Init() @@ -111,9 +110,6 @@ func (s *AssteroddScene) Render(dt time.Duration) { s.World.Systems.TextureRect.Run(dt) s.World.Systems.TextureCircle.Run(dt) - // Render all renderables with cameras - s.World.Systems.RenderCameras.Run(dt) - // Over cameras render example s.World.Systems.RenderOverlay.Run(dt) } @@ -125,7 +121,6 @@ func (s *AssteroddScene) Destroy() { s.World.Systems.CollisionHandler.Destroy() s.World.Systems.SpaceshipIntents.Destroy() s.World.Systems.MainCamera.Destroy() - s.World.Systems.PositionToSprite.Destroy() s.World.Systems.TexturePositionSmooth.Destroy() s.World.Systems.TextureRect.Destroy() s.World.Systems.TextureCircle.Destroy() diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index d5249044..e45cdf83 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -65,7 +65,6 @@ func (s *AssteroddSystem) Init() { Scales: s.Scales, Velocities: s.Velocities, Sprites: s.Sprites, - Textures: s.Textures, BoxColliders: s.BoxColliders, RigidBodies: s.RigidBodies, PlayerTags: s.PlayerTags, @@ -73,9 +72,7 @@ func (s *AssteroddSystem) Init() { Weapons: s.Weapons, SpaceshipIntents: s.SpaceshipIntents, SoundEffects: s.SoundEffects, - SmoothPositions: s.TexturePositionSmooth, Renderables: s.Renderables, - RenderOrders: s.RenderOrders, }, 300, 300, -44.9) entities.CreateSatellite(entities.CreateSatelliteManagers{ @@ -85,11 +82,9 @@ func (s *AssteroddSystem) Init() { Scales: s.Scales, Velocities: s.Velocities, Sprites: s.Sprites, - Textures: s.Textures, BoxColliders: s.BoxColliders, RigidBodies: s.RigidBodies, Renderables: s.Renderables, - RenderOrders: s.RenderOrders, }, 500, 500, 0) entities.CreateSpaceSpawner(entities.CreateSpaceSpawnerManagers{ @@ -106,11 +101,9 @@ func (s *AssteroddSystem) Init() { Scales: s.Scales, BoxColliders: s.BoxColliders, Sprites: s.Sprites, - Textures: s.Textures, WallTags: s.WallTags, RigidBodies: s.RigidBodies, Renderables: s.Renderables, - RenderOrders: s.RenderOrders, } entities.CreateWall(&wallManager, 0, -1000, 0, 5000, 1000) entities.CreateWall(&wallManager, 0, 5000, 0, 5000, 1000) @@ -132,12 +125,9 @@ func (s *AssteroddSystem) Init() { CircleColliders: s.CircleColliders, RigidBodies: s.RigidBodies, Sprites: s.Sprites, - Textures: s.Textures, BulletTags: s.BulletTags, Hps: s.Hps, - Smooth: s.TexturePositionSmooth, Renderables: s.Renderables, - RenderOrders: s.RenderOrders, }, randPos.X, randPos.Y, 0, 0, 0) // bug case diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index 6d00266f..a2c3df22 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -95,7 +95,7 @@ func (s *MainCameraSystem) Run(dt time.Duration) { //c.Camera2D.Target = playerPosition.XY if s.shouldRotate { rotation := s.Rotation.Get(entity) - c.Camera2D.Rotation = -float32(s.expDecay(float64(c.Camera2D.Rotation), rotation.Degrees(), decay, float64(dt))) + c.Camera2D.Rotation = float32(s.expDecay(float64(c.Camera2D.Rotation), -rotation.Degrees(), decay, float64(dt))) } else { c.Camera2D.Rotation = 0 } diff --git a/examples/new-api/systems/position-to-sprite.go b/examples/new-api/systems/position-to-sprite.go deleted file mode 100644 index 05066daa..00000000 --- a/examples/new-api/systems/position-to-sprite.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" - "math" - "time" -) - -// PositionToSpriteSystem updates the sprite's destination based on the position component. -// This is temporary workaround system -type PositionToSpriteSystem struct { - EntityManager *ecs.EntityManager - Position *stdcomponents.PositionComponentManager - Sprite *stdcomponents.SpriteComponentManager - SpriteMatrix *stdcomponents.SpriteMatrixComponentManager - Rotation *stdcomponents.RotationComponentManager -} - -func NewPositionToSpriteSystem() PositionToSpriteSystem { - return PositionToSpriteSystem{} -} - -func (s *PositionToSpriteSystem) Init() { -} - -func (s *PositionToSpriteSystem) Run(dt time.Duration) { - s.Sprite.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, _ int) bool { - position := s.Position.Get(entity) - sprite := s.Sprite.Get(entity) - rotation := s.Rotation.Get(entity) - - sprite.Dest.X = position.XY.X - sprite.Dest.Y = position.XY.Y - sprite.Rotation = float32(rotation.Degrees()) - - return true - }) - s.SpriteMatrix.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, _ int) bool { - position := s.Position.Get(entity) - sprite := s.SpriteMatrix.Get(entity) - rotation := s.Rotation.Get(entity) - - sprite.Dest.X = position.XY.X - sprite.Dest.Y = position.XY.Y - sprite.Rotation = rotation.Angle - - return true - }) -} - -func (s *PositionToSpriteSystem) Destroy() { -} diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index 2afb7922..5c886db4 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -44,6 +44,7 @@ type RenderOverlaySystem struct { Collisions *stdcomponents.CollisionComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager Textures *stdcomponents.RLTextureProComponentManager + frameBuffer ecs.Entity monitorWidth int monitorHeight int debugLvl int @@ -53,6 +54,16 @@ type RenderOverlaySystem struct { func (s *RenderOverlaySystem) Init() { s.monitorWidth = rl.GetScreenWidth() s.monitorHeight = rl.GetScreenHeight() + + s.frameBuffer = s.EntityManager.Create() + s.FrameBuffer2D.Create(s.frameBuffer, stdcomponents.FrameBuffer2D{ + Frame: rl.Rectangle{X: 0, Y: 0, Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)}, + Texture: rl.LoadRenderTexture(int32(s.monitorWidth), int32(s.monitorHeight)), + Layer: config.MainCameraLayer + 100, + BlendMode: rl.BlendAlpha, + Tint: rl.White, + Dst: rl.Rectangle{Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)}, + }) } func (s *RenderOverlaySystem) Run(dt time.Duration) bool { @@ -76,16 +87,19 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } } - s.FrameBuffer2D.EachEntity(func(entity ecs.Entity) bool { - frame := s.FrameBuffer2D.Get(entity) + s.Cameras.EachEntity(func(entity ecs.Entity) bool { camera := s.Cameras.Get(entity) + frame := s.FrameBuffer2D.Get(entity) switch frame.Layer { case config.MainCameraLayer: - rl.BeginTextureMode(frame.Texture) + overlayFrame := s.FrameBuffer2D.Get(s.frameBuffer) + rl.BeginTextureMode(overlayFrame.Texture) + rl.ClearBackground(rl.Blank) // Debug mode: BVH tree and dots if s.debug { rl.BeginMode2D(camera.Camera2D) + cameraRect := camera.Rect() s.CollisionChunks.EachEntity(func(e ecs.Entity) bool { chunk := s.CollisionChunks.Get(e) @@ -107,7 +121,12 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { tree.AabbNodes.EachData(func(a *stdcomponents.AABB) bool { // Simple AABB culling if s.intersects(cameraRect, a.Rect()) { - rl.DrawRectangle(int32(a.Min.X), int32(a.Min.Y), int32(a.Max.X-a.Min.X), int32(a.Max.Y-a.Min.Y), *tint) + rl.DrawRectangleRec(rl.Rectangle{ + X: a.Min.X, + Y: a.Min.Y, + Width: a.Max.X - a.Min.X, + Height: a.Max.Y - a.Min.Y, + }, *tint) } return true }) @@ -190,9 +209,9 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { rl.DrawRectangleLines(1, 1, frame.Texture.Texture.Width-1, frame.Texture.Texture.Height-1, rl.Green) rl.EndTextureMode() } + return true }) - return true } diff --git a/examples/new-api/systems/space-spawner.go b/examples/new-api/systems/space-spawner.go index 360734a6..312e7bf7 100644 --- a/examples/new-api/systems/space-spawner.go +++ b/examples/new-api/systems/space-spawner.go @@ -73,8 +73,6 @@ func (s *SpaceSpawnerSystem) Run(dt time.Duration) { Hp: s.Hp, RigidBodies: s.RigidBodies, Renderables: s.Renderables, - RenderOrders: s.RenderOrders, - Textures: s.Textures, }, pos.XY.X, pos.XY.Y, 0, 1+rand.Float32()*2, 0, 50+rand.Float32()*100) spawner.CooldownLeft = spawner.Cooldown return true diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index fcc2b2fc..d5fdd5f7 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -127,12 +127,9 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { CircleColliders: s.CircleColliders, RigidBodies: s.RigidBodies, Sprites: s.Sprites, - Textures: s.Textures, BulletTags: s.BulletTags, Hps: s.Hps, - Smooth: s.TexturePositionSmooth, Renderables: s.Renderables, - RenderOrders: s.RenderOrders, }, pos.XY.X, pos.XY.Y, angle, bulletVelocityX, bulletVelocityY) } weapon.CooldownLeft = weapon.Cooldown diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 10e021c8..eeddbe64 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -45,6 +45,7 @@ const ( FrameBuffer2DComponentId CameraComponentId TexturePositionSmoothComponentId + RenderVisibleComponentId StdLastComponentId // StdLastComponentId MUST always be the last ) diff --git a/stdcomponents/render-visible.go b/stdcomponents/render-visible.go new file mode 100644 index 00000000..7ab8b444 --- /dev/null +++ b/stdcomponents/render-visible.go @@ -0,0 +1,25 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type RenderVisible struct{} + +type RenderVisibleComponentManager = ecs.ComponentManager[RenderVisible] + +func NewRenderVisibleComponentManager() RenderVisibleComponentManager { + return ecs.NewComponentManager[RenderVisible](RenderVisibleComponentId) +} diff --git a/stdcomponents/sprite.go b/stdcomponents/sprite.go index 54f1153c..80e9ead4 100644 --- a/stdcomponents/sprite.go +++ b/stdcomponents/sprite.go @@ -21,12 +21,10 @@ import ( ) type Sprite struct { - Texture *rl.Texture2D - Frame rl.Rectangle - Origin rl.Vector2 - Tint color.RGBA - Dest rl.Rectangle - Rotation float32 + Texture *rl.Texture2D + Frame rl.Rectangle + Origin rl.Vector2 + Tint color.RGBA } type SpriteComponentManager = ecs.ComponentManager[Sprite] diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index b8712aeb..0901d925 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -176,7 +176,7 @@ func (s *CollisionDetectionSystem) setup() { R: uint8(rand.Intn(255)), G: uint8(rand.Intn(255)), B: uint8(rand.Intn(255)), - A: 20, + A: 170, }) } diff --git a/stdsystems/culling.go b/stdsystems/culling.go new file mode 100644 index 00000000..b5a97eae --- /dev/null +++ b/stdsystems/culling.go @@ -0,0 +1,79 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "time" +) + +func NewCullingSystem() CullingSystem { + return CullingSystem{} +} + +type CullingSystem struct { + Renderables *stdcomponents.RenderableComponentManager + RenderVisible *stdcomponents.RenderVisibleComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + Textures *stdcomponents.RLTextureProComponentManager + AABBs *stdcomponents.AABBComponentManager + Cameras *stdcomponents.CameraComponentManager + RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager +} + +func (s *CullingSystem) Init() { +} + +func (s *CullingSystem) Run(dt time.Duration) { + s.Cameras.EachEntity(func(entity ecs.Entity) bool { + camera := s.Cameras.Get(entity) + cameraRect := camera.Rect() + + // Collect and sort render objects + s.Renderables.EachEntity(func(entity ecs.Entity) bool { + t := s.Textures.Get(entity) + renderVisible := s.RenderVisible.Get(entity) + aabb := s.AABBs.Get(entity) + + //TODO: rework this with future new assets manager + if t != nil && t.Texture != nil { + switch camera.Culling { + case stdcomponents.Culling2DFullscreenBB: + //TODO: textureAABB + if renderVisible == nil { + if aabb != nil && s.intersects(cameraRect, aabb.Rect()) { + s.RenderVisible.Create(entity, stdcomponents.RenderVisible{}) + } + } else { + if !s.intersects(cameraRect, aabb.Rect()) { + s.RenderVisible.Remove(entity) + } + } + default: + if renderVisible == nil { + s.RenderVisible.Create(entity, stdcomponents.RenderVisible{}) + } + } + } + + return true + }) + return true + }) +} + +func (_ *CullingSystem) intersects(rect1, rect2 vectors.Rectangle) bool { + return rect1.X < rect2.X+rect2.Width && + rect1.X+rect1.Width > rect2.X && + rect1.Y < rect2.Y+rect2.Height && + rect1.Y+rect1.Height > rect2.Y +} + +func (s *CullingSystem) Destroy() { +} diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index f97a32a7..1ba9dea0 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -12,8 +12,9 @@ import ( "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" - "gomp/vectors" + "math" "slices" + "sync" "time" ) @@ -22,59 +23,52 @@ func NewRender2DCamerasSystem() Render2DCamerasSystem { } type Render2DCamerasSystem struct { - Renderables *stdcomponents.RenderableComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - Textures *stdcomponents.RLTextureProComponentManager - AABBs *stdcomponents.AABBComponentManager - Cameras *stdcomponents.CameraComponentManager - RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager - renderObjects []renderObject + Renderables *stdcomponents.RenderableComponentManager + RenderVisibles *stdcomponents.RenderVisibleComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + Textures *stdcomponents.RLTextureProComponentManager + Tints *stdcomponents.TintComponentManager + AABBs *stdcomponents.AABBComponentManager + Cameras *stdcomponents.CameraComponentManager + RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + Flips *stdcomponents.FlipComponentManager + Positions *stdcomponents.PositionComponentManager + Scales *stdcomponents.ScaleComponentManager + Rotations *stdcomponents.RotationComponentManager + renderObjects []renderObject } type renderObject struct { - texture *stdcomponents.RLTexturePro - renderable *stdcomponents.Renderable - aabb *stdcomponents.AABB - order float32 + texture stdcomponents.RLTexturePro + mask stdcomponents.CameraLayer + order float32 } func (s *Render2DCamerasSystem) Init() { - s.renderObjects = make([]renderObject, 0, s.Renderables.Len()) + s.renderObjects = make([]renderObject, 0, s.RenderVisibles.Len()) } func (s *Render2DCamerasSystem) Run(dt time.Duration) { + s.prepareRender(dt) + s.Cameras.EachEntity(func(entity ecs.Entity) bool { camera := s.Cameras.Get(entity) - cameraRect := camera.Rect() renderTexture := s.RenderTexture2D.Get(entity) // Collect and sort render objects - s.Renderables.EachEntity(func(entity ecs.Entity) bool { + s.RenderVisibles.EachEntity(func(entity ecs.Entity) bool { r := s.Renderables.Get(entity) t := s.Textures.Get(entity) o := s.RenderOrders.Get(entity) - aabb := s.AABBs.Get(entity) //TODO: rework this with future new assets manager if t != nil && t.Texture != nil { - switch camera.Culling { - case stdcomponents.Culling2DFullscreenBB: - if aabb != nil && intersects(cameraRect, aabb.Rect()) { - s.renderObjects = append(s.renderObjects, renderObject{ - texture: t, - renderable: r, - order: o.CalculatedZ, - aabb: aabb, - }) - } - default: - s.renderObjects = append(s.renderObjects, renderObject{ - texture: t, - renderable: r, - order: o.CalculatedZ, - aabb: aabb, - }) - } + s.renderObjects = append(s.renderObjects, renderObject{ + texture: *t, + mask: r.CameraMask, + order: o.CalculatedZ, + }) } return true @@ -90,7 +84,7 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { rl.ClearBackground(camera.BGColor) for _, obj := range s.renderObjects { - if camera.Layer&obj.renderable.CameraMask != 0 { + if camera.Layer&obj.mask != 0 { assert.Nil(obj.texture, "EntityTexturePro is nil") rl.DrawTexturePro(*obj.texture.Texture, obj.texture.Frame, obj.texture.Dest, obj.texture.Origin, obj.texture.Rotation, obj.texture.Tint) } @@ -104,12 +98,120 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { }) } -func intersects(rect1, rect2 vectors.Rectangle) bool { - return rect1.X < rect2.X+rect2.Width && - rect1.X+rect1.Width > rect2.X && - rect1.Y < rect2.Y+rect2.Height && - rect1.Y+rect1.Height > rect2.Y +func (s *Render2DCamerasSystem) Destroy() { } -func (s *Render2DCamerasSystem) Destroy() { +func (s *Render2DCamerasSystem) prepareRender(dt time.Duration) { + wg := new(sync.WaitGroup) + wg.Add(6) + s.prepareAnimations(wg) + go s.prepareFlips(wg) + go s.preparePositions(wg, dt) + go s.prepareRotations(wg) + go s.prepareScales(wg) + go s.prepareTints(wg) + wg.Wait() +} + +func (s *Render2DCamerasSystem) prepareAnimations(wg *sync.WaitGroup) { + defer wg.Done() + s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + texturePro := s.Textures.Get(entity) + animation := s.AnimationPlayers.Get(entity) + if animation == nil { + return true + } + frame := &texturePro.Frame + if animation.Vertical { + frame.Y += frame.Height * float32(animation.Current) + } else { + frame.X += frame.Width * float32(animation.Current) + } + return true + }) +} + +func (s *Render2DCamerasSystem) prepareFlips(wg *sync.WaitGroup) { + defer wg.Done() + s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + texturePro := s.Textures.Get(entity) + flipped := s.Flips.Get(entity) + if flipped == nil { + return true + } + if flipped.X { + texturePro.Frame.Width *= -1 + } + if flipped.Y { + texturePro.Frame.Height *= -1 + } + return true + }) +} + +func (s *Render2DCamerasSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { + defer wg.Done() + dts := dt.Seconds() + s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + texturePro := s.Textures.Get(entity) + position := s.Positions.Get(entity) + if position == nil { + return true + } + decay := 40.0 // DECAY IS TICKRATE DEPENDENT + x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) + y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) + texturePro.Dest.X = x + texturePro.Dest.Y = y + + return true + }) +} + +func (s *Render2DCamerasSystem) prepareRotations(wg *sync.WaitGroup) { + defer wg.Done() + s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + texturePro := s.Textures.Get(entity) + rotation := s.Rotations.Get(entity) + if rotation == nil { + return true + } + texturePro.Rotation = float32(rotation.Degrees()) + return true + }) +} + +func (s *Render2DCamerasSystem) prepareScales(wg *sync.WaitGroup) { + defer wg.Done() + s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + texturePro := s.Textures.Get(entity) + scale := s.Scales.Get(entity) + if scale == nil { + return true + } + texturePro.Dest.Width *= scale.XY.X + texturePro.Dest.Height *= scale.XY.Y + return true + }) +} + +func (s *Render2DCamerasSystem) prepareTints(wg *sync.WaitGroup) { + defer wg.Done() + s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + tr := s.Textures.Get(entity) + tint := s.Tints.Get(entity) + if tint == nil { + return true + } + trTint := &tr.Tint + trTint.A = tint.A + trTint.R = tint.R + trTint.G = tint.G + trTint.B = tint.B + return true + }) +} + +func (s *Render2DCamerasSystem) expDecay(a, b, decay, dt float64) float64 { + return b + (a-b)*(math.Exp(-decay*dt)) } diff --git a/stdsystems/render.go b/stdsystems/render.go index 23d1edcc..fd3d53fc 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -8,6 +8,7 @@ Donations during this file development: -===-===-===-===-===-===-===-===-===-=== <- HromRu Donated 1 500 RUB +<- MuTaToR Donated 500 RUB Thank you for your support! */ diff --git a/stdsystems/sprite-matrix.go b/stdsystems/sprite-matrix.go index b629f657..c92d6156 100644 --- a/stdsystems/sprite-matrix.go +++ b/stdsystems/sprite-matrix.go @@ -7,6 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdsystems import ( + rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" "gomp/stdcomponents" ) @@ -20,12 +21,14 @@ type SpriteMatrixSystem struct { SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager RLTexturePros *stdcomponents.RLTextureProComponentManager AnimationStates *stdcomponents.AnimationStateComponentManager + Positions *stdcomponents.PositionComponentManager } func (s *SpriteMatrixSystem) Init() {} func (s *SpriteMatrixSystem) Run() { s.SpriteMatrixes.EachEntity(func(entity ecs.Entity) bool { - spriteMatrix := s.SpriteMatrixes.Get(entity) // + spriteMatrix := s.SpriteMatrixes.Get(entity) // + position := s.Positions.Get(entity) animationState := s.AnimationStates.Get(entity) // frame := spriteMatrix.Animations[*animationState].Frame @@ -36,11 +39,11 @@ func (s *SpriteMatrixSystem) Run() { Texture: spriteMatrix.Texture, // Frame: frame, // Origin: spriteMatrix.Origin, - Dest: spriteMatrix.Dest, // + Dest: rl.Rectangle{X: position.XY.X, Y: position.XY.Y, Width: frame.Width, Height: frame.Height}, // }) } else { // Run spriteRender - tr.Dest = spriteMatrix.Dest + tr.Frame = frame } return true diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index f596bc4b..7213742a 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -26,30 +26,42 @@ func NewSpriteSystem() SpriteSystem { // SpriteSystem is a system that prepares Sprite to be rendered type SpriteSystem struct { + Positions *stdcomponents.PositionComponentManager Scales *stdcomponents.ScaleComponentManager Sprites *stdcomponents.SpriteComponentManager RLTexturePros *stdcomponents.RLTextureProComponentManager - Renderables *stdcomponents.RenderableComponentManager RenderOrder *stdcomponents.RenderOrderComponentManager } func (s *SpriteSystem) Init() {} func (s *SpriteSystem) Run() { - s.Sprites.EachEntityParallel(1, func(entity ecs.Entity, i int) bool { - sprite := s.Sprites.Get(entity) + s.Sprites.EachEntity(func(entity ecs.Entity) bool { + sprite := s.Sprites.Get(entity) // + position := s.Positions.Get(entity) scale := s.Scales.Get(entity) - tr := s.RLTexturePros.Get(entity) - tr.Texture = sprite.Texture - tr.Frame = sprite.Frame - tr.Dest = sprite.Dest - tr.Origin = rl.Vector2{ - X: sprite.Origin.X * scale.XY.X, - Y: sprite.Origin.Y * scale.XY.Y, + renderOrder := s.RenderOrder.Get(entity) + if renderOrder == nil { + renderOrder = s.RenderOrder.Create(entity, stdcomponents.RenderOrder{}) } - tr.Rotation = sprite.Rotation - tr.Tint = sprite.Tint + tr := s.RLTexturePros.Get(entity) + if tr == nil { + s.RLTexturePros.Create(entity, stdcomponents.RLTexturePro{ + Texture: sprite.Texture, // + Frame: sprite.Frame, // + Origin: rl.Vector2{ + X: sprite.Origin.X * scale.XY.X, + Y: sprite.Origin.Y * scale.XY.Y, + }, + Dest: rl.Rectangle{X: position.XY.X, Y: position.XY.Y, Width: sprite.Frame.Width, Height: sprite.Frame.Height}, // + Tint: sprite.Tint, + }) + } else { + tr.Dest.Width = sprite.Frame.Width + tr.Dest.Height = sprite.Frame.Height + tr.Tint = sprite.Tint + } return true }) } From 62cb525b7949fa3c02b92d0d937c09bbfe94cffb Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 22:23:28 +0500 Subject: [PATCH 125/196] clean culling.go --- examples/new-api/game.go | 2 +- examples/new-api/scenes/assterodd-scene.go | 22 ++++----- examples/new-api/scenes/main-scene.go | 1 - examples/new-api/systems/camera-minimap.go | 3 +- examples/new-api/systems/texture-circle.go | 3 +- examples/new-api/systems/texture-rect.go | 3 +- stdcomponents/render-visible.go | 4 +- stdcomponents/renderable.go | 1 + stdsystems/culling.go | 54 +++++++++++++--------- stdsystems/texture-position-smooth.go | 8 ++-- 10 files changed, 55 insertions(+), 46 deletions(-) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 2d152e6f..7116f7c9 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -124,8 +124,8 @@ func (g *Game) Render(dt time.Duration) { systems.Audio.Run(dt) systems.SpatialAudio.Run(dt) - systems.Culling.Run(dt) // Render all renderables with cameras + systems.Culling.Run(dt) systems.RenderCameras.Run(dt) scene.Render(dt) diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index 93f8c635..f7994a32 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -86,6 +86,16 @@ func (s *AssteroddScene) Init(world ecs.AnyWorld) { func (s *AssteroddScene) Update(dt time.Duration) gomp.SceneId { s.World.Systems.AssteroddSystem.Run(dt) + // Camera game logic flow + s.World.Systems.MainCamera.Run(dt) + s.World.Systems.Minimap.Run(dt) + + s.World.Systems.DebugInfo.Run(dt) + + // Optimized primitives + s.World.Systems.TextureRect.Run(dt) + s.World.Systems.TextureCircle.Run(dt) + return AssteroddSceneId } @@ -98,18 +108,6 @@ func (s *AssteroddScene) FixedUpdate(dt time.Duration) { } func (s *AssteroddScene) Render(dt time.Duration) { - // Camera game logic flow - s.World.Systems.MainCamera.Run(dt) - s.World.Systems.Minimap.Run(dt) - - //s.World.Systems.PositionToSprite.Run(dt) - //s.World.Systems.TexturePositionSmooth.Run(dt) - s.World.Systems.DebugInfo.Run(dt) - - // Optimized primitives - s.World.Systems.TextureRect.Run(dt) - s.World.Systems.TextureCircle.Run(dt) - // Over cameras render example s.World.Systems.RenderOverlay.Run(dt) } diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index fab7d840..e2b9d359 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -90,7 +90,6 @@ func (s *MainScene) Render(dt time.Duration) { s.World.Systems.Debug.Run() s.World.Systems.AssetLib.Run() s.World.Systems.YSort.Run() - } func (s *MainScene) Destroy() { diff --git a/examples/new-api/systems/camera-minimap.go b/examples/new-api/systems/camera-minimap.go index e58ea7b3..fbeb7afb 100644 --- a/examples/new-api/systems/camera-minimap.go +++ b/examples/new-api/systems/camera-minimap.go @@ -83,9 +83,10 @@ func (s *MinimapSystem) Run(dt time.Duration) bool { } s.Player.EachEntity(func(entity ecs.Entity) bool { playerPosition := s.Position.Get(entity) + rotation := s.Rotation.Get(entity) + c.Camera2D.Target.X = playerPosition.XY.X c.Camera2D.Target.Y = playerPosition.XY.Y - rotation := s.Rotation.Get(entity) c.Camera2D.Rotation = -float32(rotation.Degrees()) return false }) diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go index cd5c0fbe..d7f2c2ac 100644 --- a/examples/new-api/systems/texture-circle.go +++ b/examples/new-api/systems/texture-circle.go @@ -12,7 +12,6 @@ import ( "gomp/examples/new-api/components" "gomp/pkg/ecs" "gomp/stdcomponents" - "math" "time" ) @@ -36,7 +35,7 @@ func (s *TextureCircleSystem) Init() { } func (s *TextureCircleSystem) Run(dt time.Duration) { - s.Circles.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, i int) bool { + s.Circles.EachEntityParallel(128, func(entity ecs.Entity, i int) bool { circle := s.Circles.Get(entity) texture := s.Textures.Get(entity) assert.Nil(texture, "texture is nil; entity: %d", entity) diff --git a/examples/new-api/systems/texture-rect.go b/examples/new-api/systems/texture-rect.go index d7025750..658c519a 100644 --- a/examples/new-api/systems/texture-rect.go +++ b/examples/new-api/systems/texture-rect.go @@ -12,7 +12,6 @@ import ( "gomp/examples/new-api/components" "gomp/pkg/ecs" "gomp/stdcomponents" - "math" "time" ) @@ -36,7 +35,7 @@ func (s *TextureRectSystem) Init() { func (s *TextureRectSystem) Run(dt time.Duration) { // Create shallow copy of texture to draw rectangles - s.TextureRect.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, i int) bool { + s.TextureRect.EachEntityParallel(128, func(entity ecs.Entity, i int) bool { rect := s.TextureRect.Get(entity) texture := s.Textures.Get(entity) assert.Nil(texture, "texture is nil; entity: %d", entity) diff --git a/stdcomponents/render-visible.go b/stdcomponents/render-visible.go index 7ab8b444..10c4adbd 100644 --- a/stdcomponents/render-visible.go +++ b/stdcomponents/render-visible.go @@ -16,7 +16,9 @@ package stdcomponents import "gomp/pkg/ecs" -type RenderVisible struct{} +type RenderVisible struct { + Counter uint8 +} type RenderVisibleComponentManager = ecs.ComponentManager[RenderVisible] diff --git a/stdcomponents/renderable.go b/stdcomponents/renderable.go index 8ed226ed..0d60fb1e 100644 --- a/stdcomponents/renderable.go +++ b/stdcomponents/renderable.go @@ -25,6 +25,7 @@ const ( type Renderable struct { CameraMask CameraLayer Type RenderableType + Observed bool } type RenderableType uint8 diff --git a/stdsystems/culling.go b/stdsystems/culling.go index b5a97eae..cb71aff9 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -31,41 +31,51 @@ func (s *CullingSystem) Init() { } func (s *CullingSystem) Run(dt time.Duration) { + s.Renderables.EachComponentParallel(2048, func(r *stdcomponents.Renderable, i int) bool { + r.Observed = false + return true + }) s.Cameras.EachEntity(func(entity ecs.Entity) bool { camera := s.Cameras.Get(entity) cameraRect := camera.Rect() - - // Collect and sort render objects s.Renderables.EachEntity(func(entity ecs.Entity) bool { - t := s.Textures.Get(entity) - renderVisible := s.RenderVisible.Get(entity) + renderable := s.Renderables.Get(entity) + //renderVisible := s.RenderVisible.Get(entity) aabb := s.AABBs.Get(entity) - //TODO: rework this with future new assets manager - if t != nil && t.Texture != nil { - switch camera.Culling { - case stdcomponents.Culling2DFullscreenBB: - //TODO: textureAABB - if renderVisible == nil { - if aabb != nil && s.intersects(cameraRect, aabb.Rect()) { - s.RenderVisible.Create(entity, stdcomponents.RenderVisible{}) - } - } else { - if !s.intersects(cameraRect, aabb.Rect()) { - s.RenderVisible.Remove(entity) - } - } - default: - if renderVisible == nil { - s.RenderVisible.Create(entity, stdcomponents.RenderVisible{}) - } + switch camera.Culling { + case stdcomponents.Culling2DFullscreenBB: + //TODO: textureAABB + if aabb == nil { + renderable.Observed = true + return true } + if s.intersects(cameraRect, aabb.Rect()) { + renderable.Observed = true + } + + default: + renderable.Observed = true } return true }) return true }) + s.Renderables.EachEntity(func(entity ecs.Entity) bool { + renderable := s.Renderables.Get(entity) + visible := s.RenderVisible.Get(entity) + if visible == nil { + if renderable.Observed { + s.RenderVisible.Create(entity, stdcomponents.RenderVisible{}) + } + } else { + if !renderable.Observed { + s.RenderVisible.Remove(entity) + } + } + return true + }) } func (_ *CullingSystem) intersects(rect1, rect2 vectors.Rectangle) bool { diff --git a/stdsystems/texture-position-smooth.go b/stdsystems/texture-position-smooth.go index e65e739a..905025e6 100644 --- a/stdsystems/texture-position-smooth.go +++ b/stdsystems/texture-position-smooth.go @@ -32,26 +32,26 @@ func (s *TexturePositionSmoothSystem) Init() { func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { //DEBUG Temporary, TODO: remove if rl.IsKeyPressed(rl.KeyI) { - s.TexturePositionSmooth.EachComponentParallel(math.MaxInt, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(128, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothOff return true }) } if rl.IsKeyPressed(rl.KeyO) { - s.TexturePositionSmooth.EachComponentParallel(math.MaxInt, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(128, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothLerp return true }) } if rl.IsKeyPressed(rl.KeyP) { - s.TexturePositionSmooth.EachComponentParallel(math.MaxInt, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(128, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothExpDecay return true }) } //END DEBUG - s.TexturePositionSmooth.EachEntityParallel(math.MaxInt, func(entity ecs.Entity, i int) bool { + s.TexturePositionSmooth.EachEntityParallel(128, func(entity ecs.Entity, i int) bool { position := s.Position.Get(entity) texture := s.RLTexture.Get(entity) smooth := s.TexturePositionSmooth.Get(entity) From 7a029359c5227b65720afd016d1b352ae14d3a17 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 22:36:01 +0500 Subject: [PATCH 126/196] wip --- examples/new-api/game.go | 4 ++-- examples/new-api/scenes/assterodd-scene.go | 20 ++++++++++---------- stdcomponents/render-visible.go | 4 +--- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 7116f7c9..9d111984 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -124,12 +124,12 @@ func (g *Game) Render(dt time.Duration) { systems.Audio.Run(dt) systems.SpatialAudio.Run(dt) + scene.Render(dt) + // Render all renderables with cameras systems.Culling.Run(dt) systems.RenderCameras.Run(dt) - scene.Render(dt) - g.renderSystem.Run(dt) } diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index f7994a32..4575dfb0 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -86,16 +86,6 @@ func (s *AssteroddScene) Init(world ecs.AnyWorld) { func (s *AssteroddScene) Update(dt time.Duration) gomp.SceneId { s.World.Systems.AssteroddSystem.Run(dt) - // Camera game logic flow - s.World.Systems.MainCamera.Run(dt) - s.World.Systems.Minimap.Run(dt) - - s.World.Systems.DebugInfo.Run(dt) - - // Optimized primitives - s.World.Systems.TextureRect.Run(dt) - s.World.Systems.TextureCircle.Run(dt) - return AssteroddSceneId } @@ -108,6 +98,16 @@ func (s *AssteroddScene) FixedUpdate(dt time.Duration) { } func (s *AssteroddScene) Render(dt time.Duration) { + // Camera game logic flow + s.World.Systems.MainCamera.Run(dt) + s.World.Systems.Minimap.Run(dt) + + s.World.Systems.DebugInfo.Run(dt) + + // Optimized primitives + s.World.Systems.TextureRect.Run(dt) + s.World.Systems.TextureCircle.Run(dt) + // Over cameras render example s.World.Systems.RenderOverlay.Run(dt) } diff --git a/stdcomponents/render-visible.go b/stdcomponents/render-visible.go index 10c4adbd..7ab8b444 100644 --- a/stdcomponents/render-visible.go +++ b/stdcomponents/render-visible.go @@ -16,9 +16,7 @@ package stdcomponents import "gomp/pkg/ecs" -type RenderVisible struct { - Counter uint8 -} +type RenderVisible struct{} type RenderVisibleComponentManager = ecs.ComponentManager[RenderVisible] From 5d74d64c2db0e61ba3936548cf05129da5434133 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 22:54:24 +0500 Subject: [PATCH 127/196] center --- examples/new-api/systems/debug-info.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/new-api/systems/debug-info.go b/examples/new-api/systems/debug-info.go index 806c5b05..4a8ba343 100644 --- a/examples/new-api/systems/debug-info.go +++ b/examples/new-api/systems/debug-info.go @@ -165,8 +165,10 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { childAABB.Min = parentAABB.Min childAABB.Max = parentAABB.Max posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) - childCircle.CenterX = posWithOffset.X - childCircle.CenterY = posWithOffset.Y + childCircle.CenterX = 0 + childCircle.CenterY = 0 + childCircle.Origin.X = posWithOffset.X + childCircle.Origin.Y = posWithOffset.Y childCircle.Radius = col.Radius * scale.XY.X childCircle.Color = circleColor } @@ -233,12 +235,15 @@ func (s *DebugInfoSystem) spawnCircle(x float32, y float32, radius float32, circ if _, ok := s.children[e]; !ok { childEntity := s.EntityManager.Create() s.Circle.Create(childEntity, components.TextureCircle{ - CenterX: x, - CenterY: y, + CenterX: 0, + CenterY: 0, Radius: radius, Rotation: 0, - Origin: rl.Vector2{}, - Color: circleColor, + Origin: rl.Vector2{ + X: x, + Y: y, + }, + Color: circleColor, }) s.AABBs.Create(childEntity, stdcomponents.AABB{}) s.Texture.Create(childEntity, stdcomponents.RLTexturePro{}) From 861355544ebac50ac330dfeba24cbd3cbdebd324 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 23:47:10 +0500 Subject: [PATCH 128/196] mini fix temp remove interpolation circle origin to center --- examples/new-api/systems/debug-info.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/examples/new-api/systems/debug-info.go b/examples/new-api/systems/debug-info.go index 4a8ba343..806c5b05 100644 --- a/examples/new-api/systems/debug-info.go +++ b/examples/new-api/systems/debug-info.go @@ -165,10 +165,8 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { childAABB.Min = parentAABB.Min childAABB.Max = parentAABB.Max posWithOffset := pos.XY.Add(col.Offset.Mul(scale.XY)) - childCircle.CenterX = 0 - childCircle.CenterY = 0 - childCircle.Origin.X = posWithOffset.X - childCircle.Origin.Y = posWithOffset.Y + childCircle.CenterX = posWithOffset.X + childCircle.CenterY = posWithOffset.Y childCircle.Radius = col.Radius * scale.XY.X childCircle.Color = circleColor } @@ -235,15 +233,12 @@ func (s *DebugInfoSystem) spawnCircle(x float32, y float32, radius float32, circ if _, ok := s.children[e]; !ok { childEntity := s.EntityManager.Create() s.Circle.Create(childEntity, components.TextureCircle{ - CenterX: 0, - CenterY: 0, + CenterX: x, + CenterY: y, Radius: radius, Rotation: 0, - Origin: rl.Vector2{ - X: x, - Y: y, - }, - Color: circleColor, + Origin: rl.Vector2{}, + Color: circleColor, }) s.AABBs.Create(childEntity, stdcomponents.AABB{}) s.Texture.Create(childEntity, stdcomponents.RLTexturePro{}) From 88de518acbeb96053e1259cf5b5152fa7dbe57ed Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 12 Apr 2025 23:48:40 +0500 Subject: [PATCH 129/196] mini fix temp remove interpolation circle origin to center --- engine.go | 3 ++- examples/new-api/config/config.go | 1 + examples/new-api/entities/asteroid.go | 2 ++ examples/new-api/game.go | 2 +- examples/new-api/systems/camera-main.go | 10 +++++----- examples/new-api/systems/render-overlay.go | 2 +- examples/new-api/systems/space-spawner.go | 2 ++ examples/new-api/systems/texture-circle.go | 7 ++++--- stdsystems/render-2d-cameras.go | 12 ++++++------ 9 files changed, 24 insertions(+), 17 deletions(-) diff --git a/engine.go b/engine.go index 694e346a..c932b18d 100644 --- a/engine.go +++ b/engine.go @@ -16,6 +16,7 @@ Thank you for your support! package gomp import ( + "log" "time" ) @@ -72,7 +73,7 @@ func (e *Engine) Run(tickrate uint, framerate uint) { } if loops >= MaxFrameSkips { nextFixedUpdateAt = time.Now() - //log.Println("Too many updates detected") + log.Println("Too many updates detected") } // RenderAssterodd diff --git a/examples/new-api/config/config.go b/examples/new-api/config/config.go index 9614a64c..abf32ad5 100644 --- a/examples/new-api/config/config.go +++ b/examples/new-api/config/config.go @@ -26,5 +26,6 @@ const ( const ( MainCameraLayer stdcomponents.CameraLayer = 1 << iota + DebugLayer MinimapCameraLayer ) diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index b897deb1..dd732ad2 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -38,6 +38,7 @@ type CreateAsteroidManagers struct { Hp *components.HpComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager Renderables *stdcomponents.RenderableComponentManager + YSorts *stdcomponents.YSortComponentManager } func CreateAsteroid( @@ -107,6 +108,7 @@ func CreateAsteroid( Type: stdcomponents.SpriteRenderableType, CameraMask: config.MainCameraLayer | config.MinimapCameraLayer, }) + props.YSorts.Create(e, stdcomponents.YSort{}) return e } diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 9d111984..780cde93 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -99,8 +99,8 @@ func (g *Game) FixedUpdate(dt time.Duration) { var scene = g.scenes[i] var systems = &world.Systems - systems.ColliderSystem.Run(dt) systems.Velocity.Run(dt) + systems.ColliderSystem.Run(dt) systems.CollisionDetection.Run(dt) //systems.CollisionDetectionBVH.Run(dt) systems.CollisionResolution.Run(dt) diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index a2c3df22..cce96479 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -89,13 +89,13 @@ func (s *MainCameraSystem) Run(dt time.Duration) { s.Player.EachEntity(func(entity ecs.Entity) bool { playerPosition := s.Position.Get(entity) c := s.Cameras.Get(s.mainCamera) - decay := 40.0 // DECAY IS TICKRATE DEPENDENT - c.Camera2D.Target.X = float32(s.expDecay(float64(c.Camera2D.Target.X), float64(playerPosition.XY.X), decay, float64(dt))) - c.Camera2D.Target.Y = float32(s.expDecay(float64(c.Camera2D.Target.Y), float64(playerPosition.XY.Y), decay, float64(dt))) - //c.Camera2D.Target = playerPosition.XY + //decay := 40.0 // DECAY IS TICKRATE DEPENDENT + //c.Camera2D.Target.X = float32(s.expDecay(float64(c.Camera2D.Target.X), float64(playerPosition.XY.X), decay, float64(dt))) + //c.Camera2D.Target.Y = float32(s.expDecay(float64(c.Camera2D.Target.Y), float64(playerPosition.XY.Y), decay, float64(dt))) + c.Camera2D.Target = rl.Vector2(playerPosition.XY) if s.shouldRotate { rotation := s.Rotation.Get(entity) - c.Camera2D.Rotation = float32(s.expDecay(float64(c.Camera2D.Rotation), -rotation.Degrees(), decay, float64(dt))) + c.Camera2D.Rotation = -float32(rotation.Degrees()) } else { c.Camera2D.Rotation = 0 } diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index 5c886db4..b5437eb4 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -59,7 +59,7 @@ func (s *RenderOverlaySystem) Init() { s.FrameBuffer2D.Create(s.frameBuffer, stdcomponents.FrameBuffer2D{ Frame: rl.Rectangle{X: 0, Y: 0, Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)}, Texture: rl.LoadRenderTexture(int32(s.monitorWidth), int32(s.monitorHeight)), - Layer: config.MainCameraLayer + 100, + Layer: config.DebugLayer, BlendMode: rl.BlendAlpha, Tint: rl.White, Dst: rl.Rectangle{Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)}, diff --git a/examples/new-api/systems/space-spawner.go b/examples/new-api/systems/space-spawner.go index 312e7bf7..04fe974e 100644 --- a/examples/new-api/systems/space-spawner.go +++ b/examples/new-api/systems/space-spawner.go @@ -42,6 +42,7 @@ type SpaceSpawnerSystem struct { Renderables *stdcomponents.RenderableComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager Textures *stdcomponents.RLTextureProComponentManager + YSorts *stdcomponents.YSortComponentManager } func (s *SpaceSpawnerSystem) Init() {} @@ -73,6 +74,7 @@ func (s *SpaceSpawnerSystem) Run(dt time.Duration) { Hp: s.Hp, RigidBodies: s.RigidBodies, Renderables: s.Renderables, + YSorts: s.YSorts, }, pos.XY.X, pos.XY.Y, 0, 1+rand.Float32()*2, 0, 50+rand.Float32()*100) spawner.CooldownLeft = spawner.Cooldown return true diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go index d7f2c2ac..4fbc3d3a 100644 --- a/examples/new-api/systems/texture-circle.go +++ b/examples/new-api/systems/texture-circle.go @@ -41,8 +41,8 @@ func (s *TextureCircleSystem) Run(dt time.Duration) { assert.Nil(texture, "texture is nil; entity: %d", entity) texture.Texture = &s.texture.Texture - texture.Dest.X = circle.CenterX - circle.Radius - texture.Dest.Y = circle.CenterY - circle.Radius + texture.Dest.X = circle.CenterX + texture.Dest.Y = circle.CenterY texture.Dest.Width = circle.Radius * 2 texture.Dest.Height = circle.Radius * 2 texture.Frame = rl.Rectangle{ @@ -52,7 +52,8 @@ func (s *TextureCircleSystem) Run(dt time.Duration) { Height: float32(s.texture.Texture.Height), } texture.Rotation = circle.Rotation - texture.Origin = circle.Origin + texture.Origin.X = circle.Origin.X + circle.Radius + texture.Origin.Y = circle.Origin.Y + circle.Radius texture.Tint = circle.Color return true }) diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index 1ba9dea0..7d56157f 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -151,18 +151,18 @@ func (s *Render2DCamerasSystem) prepareFlips(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() - dts := dt.Seconds() + //dts := dt.Seconds() s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.Get(entity) position := s.Positions.Get(entity) if position == nil { return true } - decay := 40.0 // DECAY IS TICKRATE DEPENDENT - x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) - y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) - texturePro.Dest.X = x - texturePro.Dest.Y = y + //decay := 40.0 // DECAY IS TICKRATE DEPENDENT + //x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) + //y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) + texturePro.Dest.X = position.XY.X + texturePro.Dest.Y = position.XY.Y return true }) From d1706cd80c6135be3461e1fdce3c2c3d4fec192e Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 13 Apr 2025 00:14:08 +0300 Subject: [PATCH 130/196] update lvl --- examples/new-api/systems/asterodd.go | 2 +- stdsystems/collision-detection.go | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index e45cdf83..c724a8a0 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -110,7 +110,7 @@ func (s *AssteroddSystem) Init() { entities.CreateWall(&wallManager, -1000, -1000, 0, 1000, 7000) entities.CreateWall(&wallManager, 5000, -1000, 0, 1000, 7000) - for range 1000 { + for range 30000 { randPos := vectors.Vec2{ X: float32(rand.Intn(5000)) + float32(rand.Intn(1000))/10000, Y: float32(rand.Intn(5000)) + float32(rand.Intn(1000))/10000, diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 0901d925..ae3a401b 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -172,11 +172,12 @@ func (s *CollisionDetectionSystem) setup() { Codes: ecs.NewSlice[uint64](64), Components: ecs.NewSlice[stdcomponents.BvhComponent](64), }) + const colorbase int = 70 s.Tints.Create(chunkEntity, color.RGBA{ - R: uint8(rand.Intn(255)), - G: uint8(rand.Intn(255)), - B: uint8(rand.Intn(255)), - A: 170, + R: uint8(colorbase + rand.Intn(255-colorbase)), + G: uint8(colorbase + rand.Intn(255-colorbase)), + B: uint8(colorbase + rand.Intn(255-colorbase)), + A: 50, }) } From 90cacc7c6714be1027ed4af893b70ebf7bed7463 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 13 Apr 2025 00:19:34 +0300 Subject: [PATCH 131/196] refactor render-2d-cameras.go for better performance --- stdsystems/render-2d-cameras.go | 115 +++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 40 deletions(-) diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index 7d56157f..5d9590b5 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -23,20 +23,21 @@ func NewRender2DCamerasSystem() Render2DCamerasSystem { } type Render2DCamerasSystem struct { - Renderables *stdcomponents.RenderableComponentManager - RenderVisibles *stdcomponents.RenderVisibleComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - Textures *stdcomponents.RLTextureProComponentManager - Tints *stdcomponents.TintComponentManager - AABBs *stdcomponents.AABBComponentManager - Cameras *stdcomponents.CameraComponentManager - RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager - AnimationPlayers *stdcomponents.AnimationPlayerComponentManager - Flips *stdcomponents.FlipComponentManager - Positions *stdcomponents.PositionComponentManager - Scales *stdcomponents.ScaleComponentManager - Rotations *stdcomponents.RotationComponentManager - renderObjects []renderObject + Renderables *stdcomponents.RenderableComponentManager + RenderVisibles *stdcomponents.RenderVisibleComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + Textures *stdcomponents.RLTextureProComponentManager + Tints *stdcomponents.TintComponentManager + AABBs *stdcomponents.AABBComponentManager + Cameras *stdcomponents.CameraComponentManager + RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + Flips *stdcomponents.FlipComponentManager + Positions *stdcomponents.PositionComponentManager + Scales *stdcomponents.ScaleComponentManager + Rotations *stdcomponents.RotationComponentManager + renderObjects []renderObject + renderObjectsSorted []renderObjectSorted } type renderObject struct { @@ -45,6 +46,11 @@ type renderObject struct { order float32 } +type renderObjectSorted struct { + entity ecs.Entity + order float32 +} + func (s *Render2DCamerasSystem) Init() { s.renderObjects = make([]renderObject, 0, s.RenderVisibles.Len()) } @@ -52,50 +58,79 @@ func (s *Render2DCamerasSystem) Init() { func (s *Render2DCamerasSystem) Run(dt time.Duration) { s.prepareRender(dt) - s.Cameras.EachEntity(func(entity ecs.Entity) bool { - camera := s.Cameras.Get(entity) - renderTexture := s.RenderTexture2D.Get(entity) - - // Collect and sort render objects - s.RenderVisibles.EachEntity(func(entity ecs.Entity) bool { - r := s.Renderables.Get(entity) - t := s.Textures.Get(entity) - o := s.RenderOrders.Get(entity) - - //TODO: rework this with future new assets manager - if t != nil && t.Texture != nil { - s.renderObjects = append(s.renderObjects, renderObject{ - texture: *t, - mask: r.CameraMask, - order: o.CalculatedZ, - }) - } + // Collect and sort render objects + if cap(s.renderObjects) < s.RenderVisibles.Len() { + s.renderObjects = make([]renderObject, 0, s.RenderVisibles.Len()) + } - return true + if cap(s.renderObjectsSorted) < s.RenderVisibles.Len() { + s.renderObjectsSorted = make([]renderObjectSorted, 0, s.RenderVisibles.Len()) + } + + s.RenderVisibles.EachEntity(func(entity ecs.Entity) bool { + o := s.RenderOrders.Get(entity) + assert.NotNil(o) + + s.renderObjectsSorted = append(s.renderObjectsSorted, renderObjectSorted{ + entity: entity, + order: o.CalculatedZ, }) - slices.SortFunc(s.renderObjects, func(a, b renderObject) int { - return cmp.Compare(a.order, b.order) + return true + }) + + slices.SortFunc(s.renderObjectsSorted, func(a, b renderObjectSorted) int { + return cmp.Compare(a.order, b.order) + }) + + for i := range s.renderObjectsSorted { + obj := &s.renderObjectsSorted[i] + + t := s.Textures.Get(obj.entity) + assert.NotNil(t) + + //TODO: rework this with future new assets manager + if t.Texture == nil { + continue + } + + r := s.Renderables.Get(obj.entity) + assert.NotNil(r) + + s.renderObjects = append(s.renderObjects, renderObject{ + texture: *t, + mask: r.CameraMask, + order: obj.order, }) + } + + s.Cameras.EachEntity(func(cameraEntity ecs.Entity) bool { + camera := s.Cameras.Get(cameraEntity) + assert.NotNil(camera) + renderTexture := s.RenderTexture2D.Get(cameraEntity) + assert.NotNil(renderTexture) // Draw render objects rl.BeginTextureMode(renderTexture.Texture) rl.BeginMode2D(camera.Camera2D) rl.ClearBackground(camera.BGColor) - for _, obj := range s.renderObjects { - if camera.Layer&obj.mask != 0 { - assert.Nil(obj.texture, "EntityTexturePro is nil") - rl.DrawTexturePro(*obj.texture.Texture, obj.texture.Frame, obj.texture.Dest, obj.texture.Origin, obj.texture.Rotation, obj.texture.Tint) + for i := range s.renderObjects { + obj := &s.renderObjects[i] + if camera.Layer&obj.mask == 0 { + continue } + rl.DrawTexturePro(*obj.texture.Texture, obj.texture.Frame, obj.texture.Dest, obj.texture.Origin, obj.texture.Rotation, obj.texture.Tint) } rl.EndMode2D() rl.EndTextureMode() - s.renderObjects = s.renderObjects[:0] return true }) + + s.renderObjects = s.renderObjects[:0] + s.renderObjectsSorted = s.renderObjectsSorted[:0] } func (s *Render2DCamerasSystem) Destroy() { From 3442d49cf2cbc5e2b1ce1d69f366e2fc40e973ee Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 14 Apr 2025 10:58:10 +0300 Subject: [PATCH 132/196] breaking changes - component-manager.go rename Get() to GetUnsafe() --- examples/new-api/systems/asterodd.go | 6 +-- examples/new-api/systems/audio.go | 2 +- examples/new-api/systems/camera-main.go | 10 ++-- examples/new-api/systems/camera-minimap.go | 8 +-- examples/new-api/systems/collision-handler.go | 50 +++++++++---------- examples/new-api/systems/damping.go | 4 +- examples/new-api/systems/debug-info.go | 44 ++++++++-------- examples/new-api/systems/hp.go | 8 +-- examples/new-api/systems/player.go | 6 +-- examples/new-api/systems/render-bogdan.go | 34 ++++++------- examples/new-api/systems/render-overlay.go | 20 ++++---- examples/new-api/systems/space-spawner.go | 8 +-- examples/new-api/systems/spaceship-intents.go | 14 +++--- examples/new-api/systems/spatial-audio.go | 6 +-- examples/new-api/systems/texture-circle.go | 4 +- examples/new-api/systems/texture-rect.go | 4 +- pkg/ecs/component-manager.go | 11 +++- stdsystems/animation-spritematrix.go | 4 +- stdsystems/collider.go | 28 +++++------ stdsystems/collision-detection-bvh.go | 42 ++++++++-------- stdsystems/collision-detection-grid.go | 38 +++++++------- stdsystems/collision-detection.go | 22 ++++---- stdsystems/collision-reslution.go | 12 ++--- stdsystems/culling.go | 12 ++--- stdsystems/render-2d-cameras.go | 34 ++++++------- stdsystems/sprite-matrix.go | 6 +-- stdsystems/sprite-sheet.go | 2 +- stdsystems/sprite.go | 10 ++-- stdsystems/texture-position-smooth.go | 6 +-- stdsystems/velocity.go | 4 +- stdsystems/ysort.go | 4 +- 31 files changed, 235 insertions(+), 228 deletions(-) diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index c724a8a0..eb0eb690 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -162,7 +162,7 @@ func (s *AssteroddSystem) Init() { } func (s *AssteroddSystem) Run(dt time.Duration) { s.PlayerTags.EachEntity(func(e ecs.Entity) bool { - intents := s.SpaceshipIntents.Get(e) + intents := s.SpaceshipIntents.GetUnsafe(e) intents.MoveUp = false intents.MoveDown = false @@ -192,9 +192,9 @@ func (s *AssteroddSystem) Run(dt time.Duration) { }) s.SceneManager.EachEntity(func(e ecs.Entity) bool { - sceneManager := s.SceneManager.Get(e) + sceneManager := s.SceneManager.GetUnsafe(e) s.PlayerTags.EachEntity(func(e ecs.Entity) bool { - playerHp := s.Hps.Get(e) + playerHp := s.Hps.GetUnsafe(e) if playerHp == nil { return true } diff --git a/examples/new-api/systems/audio.go b/examples/new-api/systems/audio.go index 24411200..169aa375 100644 --- a/examples/new-api/systems/audio.go +++ b/examples/new-api/systems/audio.go @@ -37,7 +37,7 @@ func (s *AudioSystem) Init() { func (s *AudioSystem) Run(dt time.Duration) { s.SoundEffects.EachEntity(func(entity ecs.Entity) bool { - soundEffect := s.SoundEffects.Get(entity) + soundEffect := s.SoundEffects.GetUnsafe(entity) clip := soundEffect.Clip // check if clip is valid diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index cce96479..e6072db4 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -87,14 +87,14 @@ func (s *MainCameraSystem) Run(dt time.Duration) { s.shouldRotate = !s.shouldRotate } s.Player.EachEntity(func(entity ecs.Entity) bool { - playerPosition := s.Position.Get(entity) - c := s.Cameras.Get(s.mainCamera) + playerPosition := s.Position.GetUnsafe(entity) + c := s.Cameras.GetUnsafe(s.mainCamera) //decay := 40.0 // DECAY IS TICKRATE DEPENDENT //c.Camera2D.Target.X = float32(s.expDecay(float64(c.Camera2D.Target.X), float64(playerPosition.XY.X), decay, float64(dt))) //c.Camera2D.Target.Y = float32(s.expDecay(float64(c.Camera2D.Target.Y), float64(playerPosition.XY.Y), decay, float64(dt))) c.Camera2D.Target = rl.Vector2(playerPosition.XY) if s.shouldRotate { - rotation := s.Rotation.Get(entity) + rotation := s.Rotation.GetUnsafe(entity) c.Camera2D.Rotation = -float32(rotation.Degrees()) } else { c.Camera2D.Rotation = 0 @@ -104,10 +104,10 @@ func (s *MainCameraSystem) Run(dt time.Duration) { //if rl.IsWindowResized() { // width, height := rl.GetScreenWidth(), rl.GetScreenHeight() - // main := s.Cameras.Get(s.mainCamera) + // main := s.Cameras.GetUnsafe(s.mainCamera) // main.Dst = vectors.Rectangle{X: 0, Y: 0, Width: float32(width), Height: float32(height)} // - // mini := s.Cameras.Get(s.minimapCamera) + // mini := s.Cameras.GetUnsafe(s.minimapCamera) // mini.Dst = vectors.Rectangle{X: float32(width) - mini.Dst.Width, Y: float32(height) - mini.Dst.Height, Width: mini.Dst.Width, Height: mini.Dst.Height} //} diff --git a/examples/new-api/systems/camera-minimap.go b/examples/new-api/systems/camera-minimap.go index fbeb7afb..1a5041bd 100644 --- a/examples/new-api/systems/camera-minimap.go +++ b/examples/new-api/systems/camera-minimap.go @@ -63,7 +63,7 @@ func (s *MinimapSystem) Init() { } func (s *MinimapSystem) Run(dt time.Duration) bool { - c := s.Cameras.Get(s.minimapCamera) + c := s.Cameras.GetUnsafe(s.minimapCamera) if rl.IsKeyPressed(rl.KeyM) { if s.disabled { @@ -73,7 +73,7 @@ func (s *MinimapSystem) Run(dt time.Duration) bool { } else { s.disabled = true s.cameraComponent = *c - s.frameBufferComponent = *s.FrameBuffer2D.Get(s.minimapCamera) + s.frameBufferComponent = *s.FrameBuffer2D.GetUnsafe(s.minimapCamera) s.Cameras.Remove(s.minimapCamera) s.FrameBuffer2D.Remove(s.minimapCamera) } @@ -82,8 +82,8 @@ func (s *MinimapSystem) Run(dt time.Duration) bool { return false } s.Player.EachEntity(func(entity ecs.Entity) bool { - playerPosition := s.Position.Get(entity) - rotation := s.Rotation.Get(entity) + playerPosition := s.Position.GetUnsafe(entity) + rotation := s.Rotation.GetUnsafe(entity) c.Camera2D.Target.X = playerPosition.XY.X c.Camera2D.Target.Y = playerPosition.XY.Y diff --git a/examples/new-api/systems/collision-handler.go b/examples/new-api/systems/collision-handler.go index 6332fd6e..19fd1507 100644 --- a/examples/new-api/systems/collision-handler.go +++ b/examples/new-api/systems/collision-handler.go @@ -72,14 +72,14 @@ func (s *CollisionHandlerSystem) Run(dt time.Duration) { func (s *CollisionHandlerSystem) Destroy() {} func (s *CollisionHandlerSystem) checkAsteroidCollisionEnter(e1, e2 ecs.Entity) bool { - e1Tag := s.AsteroidTags.Get(e1) + e1Tag := s.AsteroidTags.GetUnsafe(e1) if e1Tag == nil { return false } - wallTag := s.WallTags.Get(e2) + wallTag := s.WallTags.GetUnsafe(e2) if wallTag != nil { - hp := s.Hps.Get(e1) + hp := s.Hps.GetUnsafe(e1) hp.Hp = 0 return true } @@ -88,20 +88,20 @@ func (s *CollisionHandlerSystem) checkAsteroidCollisionEnter(e1, e2 ecs.Entity) } func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bool { - e1Tag := s.PlayerTags.Get(e1) - e2Tag := s.PlayerTags.Get(e2) + e1Tag := s.PlayerTags.GetUnsafe(e1) + e2Tag := s.PlayerTags.GetUnsafe(e2) if e1Tag != nil { // this is a player - asteroidTag := s.AsteroidTags.Get(e2) + asteroidTag := s.AsteroidTags.GetUnsafe(e2) if asteroidTag != nil { - hp := s.Hps.Get(e1) + hp := s.Hps.GetUnsafe(e1) hp.Hp -= 1 sfxEntity := s.EntityManager.Create() - playerPos := s.Positions.Get(e1) + playerPos := s.Positions.GetUnsafe(e1) s.Positions.Create(sfxEntity, stdcomponents.Position{XY: playerPos.XY}) s.SoundEffects.Create(sfxEntity, components.SoundEffect{ @@ -116,11 +116,11 @@ func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bo return true } - //wallTag := s.WallTags.Get(e2) + //wallTag := s.WallTags.GetUnsafe(e2) //if wallTag != nil { // // reverse player movement vector - // velocity := s.Velocities.Get(e1) - // rotation := s.Rotations.Get(e1) + // velocity := s.Velocities.GetUnsafe(e1) + // rotation := s.Rotations.GetUnsafe(e1) // velocity.X *= -1 // velocity.Y *= -1 // rotation.Angle += 180 @@ -128,18 +128,18 @@ func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bo //} } else if e2Tag != nil { // this is a player - asteroidTag := s.AsteroidTags.Get(e1) + asteroidTag := s.AsteroidTags.GetUnsafe(e1) if asteroidTag != nil { - hp := s.Hps.Get(e2) + hp := s.Hps.GetUnsafe(e2) hp.Hp -= 1 return true } - //wallTag := s.WallTags.Get(e1) + //wallTag := s.WallTags.GetUnsafe(e1) //if wallTag != nil { // // reverse player movement vector - // velocity := s.Velocities.Get(e2) - // rotation := s.Rotations.Get(e2) + // velocity := s.Velocities.GetUnsafe(e2) + // rotation := s.Rotations.GetUnsafe(e2) // velocity.X *= -1 // velocity.Y *= -1 // rotation.Angle += 180 @@ -151,30 +151,30 @@ func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bo } func (s *CollisionHandlerSystem) checkBulletCollisionEnter(e1, e2 ecs.Entity) bool { - e1Tag := s.BulletTags.Get(e1) - e2Tag := s.BulletTags.Get(e2) + e1Tag := s.BulletTags.GetUnsafe(e1) + e2Tag := s.BulletTags.GetUnsafe(e2) if e1Tag != nil { // this is a bullet - bulletHp := s.Hps.Get(e1) - asteroidTag := s.AsteroidTags.Get(e2) + bulletHp := s.Hps.GetUnsafe(e1) + asteroidTag := s.AsteroidTags.GetUnsafe(e2) if asteroidTag != nil { - asteroidHp := s.Hps.Get(e2) + asteroidHp := s.Hps.GetUnsafe(e2) asteroidHp.Hp -= 1 bulletHp.Hp -= 1 return true } - wallTag := s.WallTags.Get(e2) + wallTag := s.WallTags.GetUnsafe(e2) if wallTag != nil { bulletHp.Hp = 0 return true } } else if e2Tag != nil { // this is a bullet - bulletHp := s.Hps.Get(e2) - asteroidTag := s.AsteroidTags.Get(e1) + bulletHp := s.Hps.GetUnsafe(e2) + asteroidTag := s.AsteroidTags.GetUnsafe(e1) if asteroidTag != nil { - asteroidHp := s.Hps.Get(e1) + asteroidHp := s.Hps.GetUnsafe(e1) asteroidHp.Hp -= 1 bulletHp.Hp -= 1 return true diff --git a/examples/new-api/systems/damping.go b/examples/new-api/systems/damping.go index 92090156..05ef492e 100644 --- a/examples/new-api/systems/damping.go +++ b/examples/new-api/systems/damping.go @@ -28,8 +28,8 @@ func (s *DampingSystem) Run(dt time.Duration) { dampingFactor := float32(0.98) // Damping factor for velocity s.Velocities.EachEntity(func(e ecs.Entity) bool { - velocity := s.Velocities.Get(e) - rigidbody := s.RigidBodies.Get(e) + velocity := s.Velocities.GetUnsafe(e) + rigidbody := s.RigidBodies.GetUnsafe(e) if rigidbody != nil && !rigidbody.IsStatic { velocity.X *= dampingFactor diff --git a/examples/new-api/systems/debug-info.go b/examples/new-api/systems/debug-info.go index 806c5b05..126ab105 100644 --- a/examples/new-api/systems/debug-info.go +++ b/examples/new-api/systems/debug-info.go @@ -61,10 +61,10 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { if rl.IsKeyPressed(rl.KeyF6) { if !s.debug { s.BoxColliders.EachEntity(func(e ecs.Entity) bool { - col := s.BoxColliders.Get(e) - scale := s.Scales.Get(e) - position := s.Positions.Get(e) - rotation := s.Rotations.Get(e) + col := s.BoxColliders.GetUnsafe(e) + scale := s.Scales.GetUnsafe(e) + position := s.Positions.GetUnsafe(e) + rotation := s.Rotations.GetUnsafe(e) x := position.XY.X y := position.XY.Y @@ -77,12 +77,12 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { return true }) s.CircleColliders.EachEntity(func(e ecs.Entity) bool { - col := s.CircleColliders.Get(e) - scale := s.Scales.Get(e) - pos := s.Positions.Get(e) + col := s.CircleColliders.GetUnsafe(e) + scale := s.Scales.GetUnsafe(e) + pos := s.Positions.GetUnsafe(e) circleColor := rl.DarkGreen - isSleeping := s.ColliderSleepStateComponentManager.Get(e) + isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(e) if isSleeping != nil { circleColor = rl.Blue } @@ -104,18 +104,18 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { // TODO: Parallelize this with future batches feature // Follow child to texture of parent box collider s.BoxColliders.EachEntity(func(e ecs.Entity) bool { - parentAABB := s.AABBs.Get(e) - parentPosition := s.Positions.Get(e) - col := s.BoxColliders.Get(e) - scale := s.Scales.Get(e) - rotation := s.Rotations.Get(e) + parentAABB := s.AABBs.GetUnsafe(e) + parentPosition := s.Positions.GetUnsafe(e) + col := s.BoxColliders.GetUnsafe(e) + scale := s.Scales.GetUnsafe(e) + rotation := s.Rotations.GetUnsafe(e) s.liveParents = append(s.liveParents, e) child, ok := s.children[e] if ok { - childAABB := s.AABBs.Get(child.id) - childRect := s.TextureRect.Get(child.id) + childAABB := s.AABBs.GetUnsafe(child.id) + childRect := s.TextureRect.GetUnsafe(child.id) if parentAABB != nil && childAABB != nil { childAABB.Min = parentAABB.Min @@ -146,18 +146,18 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { // TODO: Parallelize this with future batches feature // Follow child to texture of parent circle collider s.CircleColliders.EachEntity(func(e ecs.Entity) bool { - parentAABB := s.AABBs.Get(e) - pos := s.Positions.Get(e) - col := s.CircleColliders.Get(e) - scale := s.Scales.Get(e) + parentAABB := s.AABBs.GetUnsafe(e) + pos := s.Positions.GetUnsafe(e) + col := s.CircleColliders.GetUnsafe(e) + scale := s.Scales.GetUnsafe(e) s.liveParents = append(s.liveParents, e) child, ok := s.children[e] if ok { - childAABB := s.AABBs.Get(child.id) - childCircle := s.Circle.Get(child.id) + childAABB := s.AABBs.GetUnsafe(child.id) + childCircle := s.Circle.GetUnsafe(child.id) circleColor := rl.DarkGreen - isSleeping := s.ColliderSleepStateComponentManager.Get(e) + isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(e) if isSleeping != nil { circleColor = rl.Blue } diff --git a/examples/new-api/systems/hp.go b/examples/new-api/systems/hp.go index d6034466..b6907ccb 100644 --- a/examples/new-api/systems/hp.go +++ b/examples/new-api/systems/hp.go @@ -36,17 +36,17 @@ type HpSystem struct { func (s *HpSystem) Init() {} func (s *HpSystem) Run(dt time.Duration) { s.Hps.EachEntity(func(e ecs.Entity) bool { - hp := s.Hps.Get(e) + hp := s.Hps.GetUnsafe(e) if hp.Hp <= 0 { - asteroid := s.Asteroids.Get(e) - player := s.Players.Get(e) + asteroid := s.Asteroids.GetUnsafe(e) + player := s.Players.GetUnsafe(e) s.AsteroidSceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { if asteroid != nil { a.PlayerScore += hp.MaxHp } if player != nil { - playerHp := s.Hp.Get(e) + playerHp := s.Hp.GetUnsafe(e) a.PlayerHp = playerHp.Hp } return false diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index dd4c4a12..6fc82d7e 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -76,9 +76,9 @@ func (s *PlayerSystem) Run() { var speed float32 = 300 for e := range s.Controllers.EachEntity { - velocity := s.Velocities.Get(e) - flip := s.Flips.Get(e) - animationState := s.AnimationStates.Get(e) + velocity := s.Velocities.GetUnsafe(e) + flip := s.Flips.GetUnsafe(e) + animationState := s.AnimationStates.GetUnsafe(e) velocity.X = 0 velocity.Y = 0 diff --git a/examples/new-api/systems/render-bogdan.go b/examples/new-api/systems/render-bogdan.go index 90d843ea..38cd1389 100644 --- a/examples/new-api/systems/render-bogdan.go +++ b/examples/new-api/systems/render-bogdan.go @@ -82,14 +82,14 @@ func (s *RenderBogdanSystem) Run(dt time.Duration) bool { } s.render() s.ColliderBoxes.EachEntity(func(e ecs.Entity) bool { - box := s.ColliderBoxes.Get(e) - pos := s.Positions.Get(e) + box := s.ColliderBoxes.GetUnsafe(e) + pos := s.Positions.GetUnsafe(e) rl.DrawRectangleLines(int32(pos.XY.X), int32(pos.XY.Y), int32(box.WH.X), int32(box.WH.Y), rl.Red) return true }) s.Collisions.EachEntity(func(entity ecs.Entity) bool { - pos := s.Positions.Get(entity) + pos := s.Positions.GetUnsafe(entity) rl.DrawRectangle(int32(pos.XY.X), int32(pos.XY.X), 16, 16, rl.Red) return true }) @@ -110,7 +110,7 @@ func (s *RenderBogdanSystem) render() { } s.Renderables.EachEntity(func(e ecs.Entity) bool { sprite := s.SpriteMatrixes.Get(e) - renderOrder := s.RenderOrders.Get(e) + renderOrder := s.RenderOrders.GetUnsafe(e) s.renderList = append(s.renderList, renderEntry{ Entity: e, TextureId: int(sprite.Texture.ID), @@ -145,7 +145,7 @@ func (s *RenderBogdanSystem) render() { } func (s *RenderBogdanSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { - return *s.RlTexturePros.Get(e) + return *s.RlTexturePros.GetUnsafe(e) } func (s *RenderBogdanSystem) prepareRender(dt time.Duration) { @@ -163,8 +163,8 @@ func (s *RenderBogdanSystem) prepareRender(dt time.Duration) { func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - animation := s.AnimationPlayers.Get(entity) + texturePro := s.RlTexturePros.GetUnsafe(entity) + animation := s.AnimationPlayers.GetUnsafe(entity) if animation == nil { return true } @@ -181,8 +181,8 @@ func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - mirrored := s.Flips.Get(entity) + texturePro := s.RlTexturePros.GetUnsafe(entity) + mirrored := s.Flips.GetUnsafe(entity) if mirrored == nil { return true } @@ -200,8 +200,8 @@ func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Durati defer wg.Done() //dts := dt.Seconds() s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - position := s.Positions.Get(entity) + texturePro := s.RlTexturePros.GetUnsafe(entity) + position := s.Positions.GetUnsafe(entity) if position == nil { return true } @@ -218,8 +218,8 @@ func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Durati func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - rotation := s.Rotations.Get(entity) + texturePro := s.RlTexturePros.GetUnsafe(entity) + rotation := s.Rotations.GetUnsafe(entity) if rotation == nil { return true } @@ -231,8 +231,8 @@ func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - scale := s.Scales.Get(entity) + texturePro := s.RlTexturePros.GetUnsafe(entity) + scale := s.Scales.GetUnsafe(entity) if scale == nil { return true } @@ -245,8 +245,8 @@ func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareTints(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { - tr := s.RlTexturePros.Get(entity) - tint := s.Tints.Get(entity) + tr := s.RlTexturePros.GetUnsafe(entity) + tint := s.Tints.GetUnsafe(entity) if tint == nil { return true } diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index b5437eb4..d298f0d6 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -88,11 +88,11 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } s.Cameras.EachEntity(func(entity ecs.Entity) bool { - camera := s.Cameras.Get(entity) - frame := s.FrameBuffer2D.Get(entity) + camera := s.Cameras.GetUnsafe(entity) + frame := s.FrameBuffer2D.GetUnsafe(entity) switch frame.Layer { case config.MainCameraLayer: - overlayFrame := s.FrameBuffer2D.Get(s.frameBuffer) + overlayFrame := s.FrameBuffer2D.GetUnsafe(s.frameBuffer) rl.BeginTextureMode(overlayFrame.Texture) rl.ClearBackground(rl.Blank) @@ -102,20 +102,20 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { cameraRect := camera.Rect() s.CollisionChunks.EachEntity(func(e ecs.Entity) bool { - chunk := s.CollisionChunks.Get(e) + chunk := s.CollisionChunks.GetUnsafe(e) assert.NotNil(chunk) if chunk.Layer != stdcomponents.CollisionLayer(s.debugLvl) { return true } - tint := s.Tints.Get(e) + tint := s.Tints.GetUnsafe(e) assert.NotNil(tint) - position := s.Positions.Get(e) + position := s.Positions.GetUnsafe(e) assert.NotNil(position) - tree := s.BvhTrees.Get(e) + tree := s.BvhTrees.GetUnsafe(e) assert.NotNil(tree) tree.AabbNodes.EachData(func(a *stdcomponents.AABB) bool { @@ -150,9 +150,9 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { return true }) s.AABBs.EachEntity(func(e ecs.Entity) bool { - aabb := s.AABBs.Get(e) + aabb := s.AABBs.GetUnsafe(e) clr := rl.Green - isSleeping := s.ColliderSleepStateComponentManager.Get(e) + isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(e) if isSleeping != nil { clr = rl.Blue } @@ -167,7 +167,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { return true }) s.Collisions.EachEntity(func(entity ecs.Entity) bool { - pos := s.Positions.Get(entity) + pos := s.Positions.GetUnsafe(entity) rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) return true }) diff --git a/examples/new-api/systems/space-spawner.go b/examples/new-api/systems/space-spawner.go index 04fe974e..36163858 100644 --- a/examples/new-api/systems/space-spawner.go +++ b/examples/new-api/systems/space-spawner.go @@ -48,20 +48,20 @@ type SpaceSpawnerSystem struct { func (s *SpaceSpawnerSystem) Init() {} func (s *SpaceSpawnerSystem) Run(dt time.Duration) { s.SpaceSpawners.EachEntity(func(e ecs.Entity) bool { - position := s.Positions.Get(e) - velocity := s.Velocities.Get(e) + position := s.Positions.GetUnsafe(e) + velocity := s.Velocities.GetUnsafe(e) if position.XY.X > 5000 || position.XY.X < 0 { velocity.X = -velocity.X } - spawner := s.SpaceSpawners.Get(e) + spawner := s.SpaceSpawners.GetUnsafe(e) if spawner.CooldownLeft > 0 { spawner.CooldownLeft -= dt return true } - pos := s.Positions.Get(e) + pos := s.Positions.GetUnsafe(e) entities.CreateAsteroid(entities.CreateAsteroidManagers{ EntityManager: s.EntityManager, Positions: s.Positions, diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index d5fdd5f7..c8ca1b24 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -63,13 +63,13 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) s.SpaceshipIntents.EachEntity(func(entity ecs.Entity) bool { - intent := s.SpaceshipIntents.Get(entity) - vel := s.Velocities.Get(entity) - rot := s.Rotations.Get(entity) - pos := s.Positions.Get(entity) - weapon := s.Weapons.Get(entity) - hp := s.Hps.Get(entity) - flySfx := s.SoundEffects.Get(entity) + intent := s.SpaceshipIntents.GetUnsafe(entity) + vel := s.Velocities.GetUnsafe(entity) + rot := s.Rotations.GetUnsafe(entity) + pos := s.Positions.GetUnsafe(entity) + weapon := s.Weapons.GetUnsafe(entity) + hp := s.Hps.GetUnsafe(entity) + flySfx := s.SoundEffects.GetUnsafe(entity) if intent.RotateLeft { rot.Angle -= rotateSpeed * vectors.Radians(dtSec) diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go index a9e45037..9f54e5bb 100644 --- a/examples/new-api/systems/spatial-audio.go +++ b/examples/new-api/systems/spatial-audio.go @@ -50,14 +50,14 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { return } - playerPos := s.Positions.Get(player) + playerPos := s.Positions.GetUnsafe(player) if playerPos == nil { return } s.SoundEffects.EachEntity(func(entity ecs.Entity) bool { - soundEffect := s.SoundEffects.Get(entity) + soundEffect := s.SoundEffects.GetUnsafe(entity) clip := soundEffect.Clip @@ -69,7 +69,7 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { return true } - position := s.Positions.Get(entity) + position := s.Positions.GetUnsafe(entity) if position == nil { return true diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go index 4fbc3d3a..3a00dfc7 100644 --- a/examples/new-api/systems/texture-circle.go +++ b/examples/new-api/systems/texture-circle.go @@ -36,8 +36,8 @@ func (s *TextureCircleSystem) Init() { func (s *TextureCircleSystem) Run(dt time.Duration) { s.Circles.EachEntityParallel(128, func(entity ecs.Entity, i int) bool { - circle := s.Circles.Get(entity) - texture := s.Textures.Get(entity) + circle := s.Circles.GetUnsafe(entity) + texture := s.Textures.GetUnsafe(entity) assert.Nil(texture, "texture is nil; entity: %d", entity) texture.Texture = &s.texture.Texture diff --git a/examples/new-api/systems/texture-rect.go b/examples/new-api/systems/texture-rect.go index 658c519a..52630e4b 100644 --- a/examples/new-api/systems/texture-rect.go +++ b/examples/new-api/systems/texture-rect.go @@ -36,8 +36,8 @@ func (s *TextureRectSystem) Init() { func (s *TextureRectSystem) Run(dt time.Duration) { // Create shallow copy of texture to draw rectangles s.TextureRect.EachEntityParallel(128, func(entity ecs.Entity, i int) bool { - rect := s.TextureRect.Get(entity) - texture := s.Textures.Get(entity) + rect := s.TextureRect.GetUnsafe(entity) + texture := s.Textures.GetUnsafe(entity) assert.Nil(texture, "texture is nil; entity: %d", entity) texture.Texture = &s.texture.Texture diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index f51bd246..1c26a7a2 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -8,6 +8,7 @@ Donations during this file development: -===-===-===-===-===-===-===-===-===-=== <- Hininn Donated 2 000 RUB +<- Сосисочник Паша Donated 77 RUB Thank you for your support! */ @@ -135,7 +136,10 @@ func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { return component } -func (c *ComponentManager[T]) Get(entity Entity) (component *T) { +/* +GetUnsafe - is not thread safe. DO NOT store the pointer to the value anywhere, because it might be changed anytime with Create or Remove operations. +*/ +func (c *ComponentManager[T]) GetUnsafe(entity Entity) (component *T) { assert.True(c.isInitialized, "ComponentManager should be created with NewComponentManager()") index, ok := c.lookup.Get(entity) @@ -147,6 +151,9 @@ func (c *ComponentManager[T]) Get(entity Entity) (component *T) { } func (c *ComponentManager[T]) Set(entity Entity, value T) *T { + c.mx.Lock() + defer c.mx.Unlock() + assert.True(c.isInitialized, "ComponentManager should be created with NewComponentManager()") index, ok := c.lookup.Get(entity) @@ -325,7 +332,7 @@ func (c *ComponentManager[T]) getChangesBinary(source *PagedArray[Entity]) Compo assert.True(e != nil) entId := *e assert.True(c.Has(entId)) - components = append(components, *c.Get(entId)) + components = append(components, *c.GetUnsafe(entId)) entities = append(entities, entId) return true }) diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index 90b7dbd0..62bd10c6 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -26,13 +26,13 @@ type AnimationSpriteMatrixSystem struct { func (s *AnimationSpriteMatrixSystem) Init() {} func (s *AnimationSpriteMatrixSystem) Run() { s.AnimationPlayers.EachEntity(func(e ecs.Entity) bool { - animationPlayer := s.AnimationPlayers.Get(e) + animationPlayer := s.AnimationPlayers.GetUnsafe(e) spriteMatrix := s.SpriteMatrixes.Get(e) if spriteMatrix == nil { return true } - animationStatePtr := s.AnimationStates.Get(e) + animationStatePtr := s.AnimationStates.GetUnsafe(e) if animationStatePtr == nil { return true } diff --git a/stdsystems/collider.go b/stdsystems/collider.go index be98142f..0512be1a 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -41,9 +41,9 @@ type ColliderSystem struct { func (s *ColliderSystem) Init() {} func (s *ColliderSystem) Run(dt time.Duration) { s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { - boxCollider := s.BoxColliders.Get(entity) + boxCollider := s.BoxColliders.GetUnsafe(entity) - genCollider := s.GenericColliders.Get(entity) + genCollider := s.GenericColliders.GetUnsafe(entity) if genCollider == nil { genCollider = s.GenericColliders.Create(entity, stdcomponents.GenericCollider{}) } @@ -54,10 +54,10 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider.Shape = stdcomponents.BoxColliderShape genCollider.AllowSleep = boxCollider.AllowSleep - position := s.Positions.Get(entity) - scale := s.Scales.Get(entity) - rotation := s.Rotations.Get(entity) - aabb := s.AABB.Get(entity) + position := s.Positions.GetUnsafe(entity) + scale := s.Scales.GetUnsafe(entity) + rotation := s.Rotations.GetUnsafe(entity) + aabb := s.AABB.GetUnsafe(entity) if aabb == nil { aabb = s.AABB.Create(entity, stdcomponents.AABB{}) } @@ -82,9 +82,9 @@ func (s *ColliderSystem) Run(dt time.Duration) { }) s.CircleColliders.EachEntity(func(entity ecs.Entity) bool { - circleCollider := s.CircleColliders.Get(entity) + circleCollider := s.CircleColliders.GetUnsafe(entity) - genCollider := s.GenericColliders.Get(entity) + genCollider := s.GenericColliders.GetUnsafe(entity) if genCollider == nil { genCollider = s.GenericColliders.Create(entity, stdcomponents.GenericCollider{}) } @@ -96,9 +96,9 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider.Shape = stdcomponents.CircleColliderShape genCollider.AllowSleep = circleCollider.AllowSleep - position := s.Positions.Get(entity) - scale := s.Scales.Get(entity) - aabb := s.AABB.Get(entity) + position := s.Positions.GetUnsafe(entity) + scale := s.Scales.GetUnsafe(entity) + aabb := s.AABB.GetUnsafe(entity) if aabb == nil { aabb = s.AABB.Create(entity, stdcomponents.AABB{}) } @@ -112,17 +112,17 @@ func (s *ColliderSystem) Run(dt time.Duration) { }) s.GenericColliders.EachEntity(func(entity ecs.Entity) bool { - genCollider := s.GenericColliders.Get(entity) + genCollider := s.GenericColliders.GetUnsafe(entity) if genCollider.AllowSleep { shouldSleep := true - velocity := s.Velocities.Get(entity) + velocity := s.Velocities.GetUnsafe(entity) if velocity != nil { if velocity.Vec2().LengthSquared() != 0 { shouldSleep = false } } - isSleeping := s.ColliderSleepStateComponentManager.Get(entity) + isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(entity) if shouldSleep { if isSleeping == nil { isSleeping = s.ColliderSleepStateComponentManager.Create(entity, stdcomponents.ColliderSleepState{}) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 28eddbe6..bd855931 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -79,8 +79,8 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { // Fill trees s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { - aabb := s.AABB.Get(entity) - layer := s.GenericCollider.Get(entity).Layer + aabb := s.AABB.GetUnsafe(entity) + layer := s.GenericCollider.GetUnsafe(entity).Layer treeId, exists := s.treesLookup[layer] if !exists { @@ -174,8 +174,8 @@ func (s *CollisionDetectionBVHSystem) registerCollisionEvents() { s.activeCollisions[pair] = proxy } else { proxy := s.activeCollisions[pair] - collision := s.Collisions.Get(proxy) - position := s.Positions.Get(proxy) + collision := s.Collisions.GetUnsafe(proxy) + position := s.Positions.GetUnsafe(proxy) collision.State = stdcomponents.CollisionStateStay collision.Depth = event.depth collision.Normal = event.normal @@ -189,15 +189,15 @@ func (s *CollisionDetectionBVHSystem) registerCollisionEvents() { } func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, result []ecs.Entity) []ecs.Entity { - colliderA := s.GenericCollider.Get(entityA) + colliderA := s.GenericCollider.GetUnsafe(entityA) if colliderA.AllowSleep { - isSleeping := s.ColliderSleepStateComponentManager.Get(entityA) + isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(entityA) if isSleeping != nil { return result } } - aabb := s.AABB.Get(entityA) + aabb := s.AABB.GetUnsafe(entityA) // Iterate through all trees for treeIndex := range s.trees { @@ -221,14 +221,14 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE continue } - colliderA := s.GenericCollider.Get(entityA) - colliderB := s.GenericCollider.Get(entityB) - posA := s.Positions.Get(entityA) - posB := s.Positions.Get(entityB) - scaleA := s.Scales.Get(entityA) - scaleB := s.Scales.Get(entityB) - rotA := s.Rotations.Get(entityA) - rotB := s.Rotations.Get(entityB) + colliderA := s.GenericCollider.GetUnsafe(entityA) + colliderB := s.GenericCollider.GetUnsafe(entityB) + posA := s.Positions.GetUnsafe(entityA) + posB := s.Positions.GetUnsafe(entityB) + scaleA := s.Scales.GetUnsafe(entityA) + scaleB := s.Scales.GetUnsafe(entityB) + rotA := s.Rotations.GetUnsafe(entityA) + rotB := s.Rotations.GetUnsafe(entityB) transformA := stdcomponents.Transform2d{ Position: posA.XY, Rotation: rotA.Angle, @@ -240,8 +240,8 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE Scale: scaleB.XY, } - circleA := s.CircleColliders.Get(entityA) - circleB := s.CircleColliders.Get(entityB) + circleA := s.CircleColliders.GetUnsafe(entityA) + circleB := s.CircleColliders.GetUnsafe(entityB) if circleA != nil && circleB != nil { radiusA := circleA.Radius * scaleA.XY.X radiusB := circleB.Radius * scaleB.XY.X @@ -282,7 +282,7 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE func (s *CollisionDetectionBVHSystem) processExitStates() { for pair, proxy := range s.activeCollisions { if _, exists := s.currentCollisions[pair]; !exists { - collision := s.Collisions.Get(proxy) + collision := s.Collisions.GetUnsafe(proxy) if collision.State == stdcomponents.CollisionStateExit { delete(s.activeCollisions, pair) s.EntityManager.Delete(proxy) @@ -296,11 +296,11 @@ func (s *CollisionDetectionBVHSystem) processExitStates() { func (s *CollisionDetectionBVHSystem) getGjkCollider(collider *stdcomponents.GenericCollider, entity ecs.Entity) gjk.AnyCollider { switch collider.Shape { case stdcomponents.BoxColliderShape: - return s.BoxColliders.Get(entity) + return s.BoxColliders.GetUnsafe(entity) case stdcomponents.CircleColliderShape: - return s.CircleColliders.Get(entity) + return s.CircleColliders.GetUnsafe(entity) case stdcomponents.PolygonColliderShape: - return s.PolygonColliders.Get(entity) + return s.PolygonColliders.GetUnsafe(entity) default: panic("unsupported collider shape") } diff --git a/stdsystems/collision-detection-grid.go b/stdsystems/collision-detection-grid.go index 8cfcf733..bfa5cb21 100644 --- a/stdsystems/collision-detection-grid.go +++ b/stdsystems/collision-detection-grid.go @@ -90,10 +90,10 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { // Build spatial buckets and entity-to-cell map s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { - position := s.Positions.Get(entity) - scale := s.Scales.Get(entity) + position := s.Positions.GetUnsafe(entity) + scale := s.Scales.GetUnsafe(entity) - collider := s.GenericCollider.Get(entity) + collider := s.GenericCollider.GetUnsafe(entity) cellX := int(position.XY.X-(collider.Offset.X*scale.XY.X)) / s.cellSizeX cellY := int(position.XY.Y-(collider.Offset.Y*scale.XY.Y)) / s.cellSizeY cell := stdcomponents.SpatialIndex{X: cellX, Y: cellY} @@ -105,9 +105,9 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { // Precompute AABBs for box colliders s.aabbs = make(map[ecs.Entity]aabb, s.BoxColliders.Len()) s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { - position := s.Positions.Get(entity) - collider := s.BoxColliders.Get(entity) - scale := s.Scales.Get(entity) + position := s.Positions.GetUnsafe(entity) + collider := s.BoxColliders.GetUnsafe(entity) + scale := s.Scales.GetUnsafe(entity) newAABB := aabb{ Left: position.XY.X - (collider.Offset.X * scale.XY.X), Right: position.XY.X + (collider.WH.X-collider.Offset.X)*scale.XY.X, @@ -140,9 +140,9 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { s.activeCollisions[pair] = proxy } else { proxy := s.activeCollisions[pair] - s.Collisions.Get(proxy).State = stdcomponents.CollisionStateStay - s.Positions.Get(proxy).XY.X = event.position.X - s.Positions.Get(proxy).XY.Y = event.position.Y + s.Collisions.GetUnsafe(proxy).State = stdcomponents.CollisionStateStay + s.Positions.GetUnsafe(proxy).XY.X = event.position.X + s.Positions.GetUnsafe(proxy).XY.Y = event.position.Y } } close(doneChan) @@ -171,7 +171,7 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { defer wg.Done() for _, entityA := range entities[start:end] { - collider := s.GenericCollider.Get(entityA) + collider := s.GenericCollider.GetUnsafe(entityA) switch collider.Shape { case stdcomponents.BoxColliderShape: @@ -191,7 +191,7 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { <-doneChan // Wait for result collector //s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { - // collider := s.GenericCollider.Get(entity) + // collider := s.GenericCollider.GetUnsafe(entity) // // switch collider.Shape { // case stdcomponents.BoxColliderShape: @@ -220,16 +220,16 @@ func (s *CollisionDetectionGridSystem) registerCollision(entityA, entityB ecs.En s.Positions.Create(proxy, stdcomponents.Position{XY: vectors.Vec2{X: posX, Y: posY}}) s.activeCollisions[pair] = proxy } else { - s.Collisions.Get(s.activeCollisions[pair]).State = stdcomponents.CollisionStateStay - s.Positions.Get(s.activeCollisions[pair]).XY.X = posX - s.Positions.Get(s.activeCollisions[pair]).XY.Y = posY + s.Collisions.GetUnsafe(s.activeCollisions[pair]).State = stdcomponents.CollisionStateStay + s.Positions.GetUnsafe(s.activeCollisions[pair]).XY.X = posX + s.Positions.GetUnsafe(s.activeCollisions[pair]).XY.Y = posY } } func (s *CollisionDetectionGridSystem) processExitStates() { for pair, proxy := range s.activeCollisions { if _, exists := s.currentCollisions[pair]; !exists { - collision := s.Collisions.Get(proxy) + collision := s.Collisions.GetUnsafe(proxy) if collision.State == stdcomponents.CollisionStateExit { delete(s.activeCollisions, pair) s.EntityManager.Delete(proxy) @@ -241,9 +241,9 @@ func (s *CollisionDetectionGridSystem) processExitStates() { } func (s *CollisionDetectionGridSystem) boxToXCollision(entityA ecs.Entity, collisionChan chan<- CollisionEvent) { - position1 := s.Positions.Get(entityA) + position1 := s.Positions.GetUnsafe(entityA) spatialIndex1 := s.entityToCell[entityA] - genericCollider1 := s.GenericCollider.Get(entityA) + genericCollider1 := s.GenericCollider.GetUnsafe(entityA) var nearByIndexes = [9]stdcomponents.SpatialIndex{ {spatialIndex1.X, spatialIndex1.Y}, @@ -265,14 +265,14 @@ func (s *CollisionDetectionGridSystem) boxToXCollision(entityA ecs.Entity, colli } // Broad Phase - genericCollider2 := s.GenericCollider.Get(entityB) + genericCollider2 := s.GenericCollider.GetUnsafe(entityB) if genericCollider1.Mask&(1< Date: Mon, 14 Apr 2025 10:58:54 +0300 Subject: [PATCH 133/196] component-manager.go add safe Get() method --- pkg/ecs/component-manager.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 1c26a7a2..6794de8b 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -150,6 +150,20 @@ func (c *ComponentManager[T]) GetUnsafe(entity Entity) (component *T) { return c.components.Get(index) } +func (c *ComponentManager[T]) Get(entity Entity) (component T, ok bool) { + c.mx.Lock() + defer c.mx.Unlock() + + assert.True(c.isInitialized, "ComponentManager should be created with NewComponentManager()") + + index, ok := c.lookup.Get(entity) + if !ok { + return component, false + } + + return c.components.GetValue(index), true +} + func (c *ComponentManager[T]) Set(entity Entity, value T) *T { c.mx.Lock() defer c.mx.Unlock() From bc754c449a051ad0f3ed412da6bcaa540ebf86b8 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 14 Apr 2025 11:02:24 +0300 Subject: [PATCH 134/196] breaking changes component-manager.go rename Remove() to Delete() --- examples/new-api/systems/camera-minimap.go | 4 ++-- pkg/ecs/component-manager-shared.go | 2 +- pkg/ecs/component-manager.go | 6 +++--- pkg/ecs/entity-manager.go | 2 +- stdsystems/collider.go | 2 +- stdsystems/culling.go | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/new-api/systems/camera-minimap.go b/examples/new-api/systems/camera-minimap.go index 1a5041bd..f6c2007c 100644 --- a/examples/new-api/systems/camera-minimap.go +++ b/examples/new-api/systems/camera-minimap.go @@ -74,8 +74,8 @@ func (s *MinimapSystem) Run(dt time.Duration) bool { s.disabled = true s.cameraComponent = *c s.frameBufferComponent = *s.FrameBuffer2D.GetUnsafe(s.minimapCamera) - s.Cameras.Remove(s.minimapCamera) - s.FrameBuffer2D.Remove(s.minimapCamera) + s.Cameras.Delete(s.minimapCamera) + s.FrameBuffer2D.Delete(s.minimapCamera) } } if s.disabled { diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 336c715e..9d9f2da1 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -164,7 +164,7 @@ func (c *SharedComponentManager[T]) Set(entity Entity, instanceId SharedComponen return c.components.Get(componentIndex) } -func (c *SharedComponentManager[T]) Remove(entity Entity) { +func (c *SharedComponentManager[T]) Delete(entity Entity) { c.mx.Lock() defer c.mx.Unlock() diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 6794de8b..96f731b5 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -31,7 +31,7 @@ type AnyComponentListPtr interface{} type AnyComponentManagerPtr interface { Id() ComponentId - Remove(Entity) + Delete(Entity) Clean() Has(Entity) bool PatchAdd(Entity) @@ -182,7 +182,7 @@ func (c *ComponentManager[T]) Set(entity Entity, value T) *T { return component } -func (c *ComponentManager[T]) Remove(entity Entity) { +func (c *ComponentManager[T]) Delete(entity Entity) { c.mx.Lock() defer c.mx.Unlock() @@ -325,7 +325,7 @@ func (c *ComponentManager[T]) PatchApply(patch ComponentPatch) { deleted := patch.Deleted components = c.decoder(deleted.Components) for i := range deleted.Len { - c.Remove(deleted.Entities[i]) + c.Delete(deleted.Entities[i]) } } diff --git a/pkg/ecs/entity-manager.go b/pkg/ecs/entity-manager.go index 4dcdff87..eb2bf1ad 100644 --- a/pkg/ecs/entity-manager.go +++ b/pkg/ecs/entity-manager.go @@ -85,7 +85,7 @@ func (e *EntityManager) Delete(entity Entity) { e.mx.Lock() defer e.mx.Unlock() e.componentBitSet.AllSet(entity, func(id ComponentId) bool { - e.components[id].Remove(entity) + e.components[id].Delete(entity) return true }) diff --git a/stdsystems/collider.go b/stdsystems/collider.go index 0512be1a..fbcfbe4f 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -129,7 +129,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { } } else { if isSleeping != nil { - s.ColliderSleepStateComponentManager.Remove(entity) + s.ColliderSleepStateComponentManager.Delete(entity) } } } diff --git a/stdsystems/culling.go b/stdsystems/culling.go index c3de47b9..26210ab1 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -71,7 +71,7 @@ func (s *CullingSystem) Run(dt time.Duration) { } } else { if !renderable.Observed { - s.RenderVisible.Remove(entity) + s.RenderVisible.Delete(entity) } } return true From f8be29b36177a09ececbd778a3a67a8d9ad84966 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 14 Apr 2025 14:33:01 +0300 Subject: [PATCH 135/196] breaking changes component-manager.go each parallel batchsize -> numWorkers --- examples/new-api/game.go | 5 +- examples/new-api/systems/camera-main.go | 4 +- examples/new-api/systems/texture-circle.go | 6 +- examples/new-api/systems/texture-rect.go | 5 +- pkg/ecs/component-manager-shared.go | 14 ++--- pkg/ecs/component-manager.go | 20 +++--- pkg/ecs/paged-array.go | 72 ++++++++-------------- pkg/ecs/slice.go | 51 +++++++-------- stdsystems/collision-detection.go | 16 ++--- stdsystems/render-2d-cameras.go | 15 +++-- stdsystems/texture-position-smooth.go | 11 ++-- 11 files changed, 104 insertions(+), 115 deletions(-) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 780cde93..798bf585 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -77,6 +77,8 @@ func (g *Game) Init() { systems.AssetLib.Init() systems.Audio.Init() systems.SpatialAudio.Init() + systems.RenderCameras.Init() + systems.Culling.Init() systems.TexturePositionSmooth.Init() scene.Init(world) @@ -154,8 +156,9 @@ func (g *Game) Destroy() { systems.AssetLib.Destroy() systems.Audio.Destroy() systems.SpatialAudio.Destroy() - systems.TexturePositionSmooth.Destroy() + systems.RenderCameras.Destroy() systems.Culling.Destroy() + systems.TexturePositionSmooth.Destroy() world.Destroy() } diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index e6072db4..a8388c13 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -60,7 +60,7 @@ func (s *MainCameraSystem) Init() { Layer: config.MainCameraLayer, Order: 0, Culling: stdcomponents.Culling2DFullscreenBB, - BlendMode: rl.BlendAlpha, + BlendMode: rl.BlendAlphaPremultiply, BGColor: color.RGBA{R: 0, G: 0, B: 0, A: 255}, Tint: color.RGBA{R: 255, G: 255, B: 255, A: 255}, }) @@ -69,7 +69,7 @@ func (s *MainCameraSystem) Init() { Frame: rl.Rectangle{X: 0, Y: 0, Width: float32(width), Height: float32(height)}, Texture: rl.LoadRenderTexture(int32(width), int32(height)), Layer: config.MainCameraLayer, - BlendMode: rl.BlendAlpha, + BlendMode: rl.BlendColor, Rotation: 0, Tint: rl.White, Dst: rl.Rectangle{Width: float32(width), Height: float32(height)}, diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go index 3a00dfc7..fcf20454 100644 --- a/examples/new-api/systems/texture-circle.go +++ b/examples/new-api/systems/texture-circle.go @@ -12,6 +12,7 @@ import ( "gomp/examples/new-api/components" "gomp/pkg/ecs" "gomp/stdcomponents" + "runtime" "time" ) @@ -23,6 +24,8 @@ type TextureCircleSystem struct { Circles *components.PrimitiveCircleComponentManager Textures *stdcomponents.RLTextureProComponentManager texture rl.RenderTexture2D + + numWorkers int } func (s *TextureCircleSystem) Init() { @@ -32,10 +35,11 @@ func (s *TextureCircleSystem) Init() { rl.DrawCircle(circleRadius, circleRadius, circleRadius, rl.White) rl.EndTextureMode() s.texture = texture + s.numWorkers = runtime.NumCPU() - 2 } func (s *TextureCircleSystem) Run(dt time.Duration) { - s.Circles.EachEntityParallel(128, func(entity ecs.Entity, i int) bool { + s.Circles.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, i int) bool { circle := s.Circles.GetUnsafe(entity) texture := s.Textures.GetUnsafe(entity) assert.Nil(texture, "texture is nil; entity: %d", entity) diff --git a/examples/new-api/systems/texture-rect.go b/examples/new-api/systems/texture-rect.go index 52630e4b..2ceb1d33 100644 --- a/examples/new-api/systems/texture-rect.go +++ b/examples/new-api/systems/texture-rect.go @@ -12,6 +12,7 @@ import ( "gomp/examples/new-api/components" "gomp/pkg/ecs" "gomp/stdcomponents" + "runtime" "time" ) @@ -23,6 +24,7 @@ type TextureRectSystem struct { TextureRect *components.TextureRectComponentManager Textures *stdcomponents.RLTextureProComponentManager texture rl.RenderTexture2D + numWorkers int } func (s *TextureRectSystem) Init() { @@ -31,11 +33,12 @@ func (s *TextureRectSystem) Init() { rl.ClearBackground(rl.White) rl.EndTextureMode() s.texture = texture + s.numWorkers = runtime.NumCPU() - 2 } func (s *TextureRectSystem) Run(dt time.Duration) { // Create shallow copy of texture to draw rectangles - s.TextureRect.EachEntityParallel(128, func(entity ecs.Entity, i int) bool { + s.TextureRect.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, i int) bool { rect := s.TextureRect.GetUnsafe(entity) texture := s.Textures.GetUnsafe(entity) assert.Nil(texture, "texture is nil; entity: %d", entity) diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 9d9f2da1..00d8dbde 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -242,25 +242,25 @@ func (c *SharedComponentManager[T]) Each(yield func(Entity, *T) bool) { // Iterators Parallel // ======================================================== -func (c *SharedComponentManager[T]) EachComponentParallel(batchSize int, yield func(*T, int) bool) { +func (c *SharedComponentManager[T]) EachComponentParallel(numWorkers int, yield func(*T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllDataParallel(batchSize, yield) + c.components.AllDataParallel(numWorkers, yield) } -func (c *SharedComponentManager[T]) EachEntityParallel(batchSize int, yield func(Entity, int) bool) { +func (c *SharedComponentManager[T]) EachEntityParallel(numWorkers int, yield func(Entity, int) bool) { c.assertBegin() defer c.assertEnd() - c.entities.AllDataValueParallel(batchSize, yield) + c.entities.AllDataValueParallel(numWorkers, yield) } -func (c *SharedComponentManager[T]) EachParallel(yield func(Entity, *T) bool) { +func (c *SharedComponentManager[T]) EachParallel(numWorkers int, yield func(Entity, *T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllParallel(func(i int, t *T) bool { + c.components.AllParallel(numWorkers, func(i int, t *T, workerId int) bool { entity := c.entities.Get(i) entId := *entity - shouldContinue := yield(entId, t) + shouldContinue := yield(entId, t, workerId) return shouldContinue }) } diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 96f731b5..cff5d0a4 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -16,9 +16,8 @@ Thank you for your support! package ecs import ( - "sync" - "github.com/negrel/assert" + "sync" ) // ================ @@ -65,7 +64,6 @@ func NewComponentManager[T any](id ComponentId) ComponentManager[T] { } type ComponentManager[T any] struct { - mx sync.Mutex components PagedArray[T] entities PagedArray[Entity] lookup PagedMap[Entity, int] @@ -85,6 +83,8 @@ type ComponentManager[T any] struct { encoder func([]T) []byte decoder func([]byte) []T + + mx sync.Mutex } // ComponentChanges with byte encoded Components @@ -260,25 +260,25 @@ func (c *ComponentManager[T]) Each(yield func(Entity, *T) bool) { // Iterators Parallel // ======================================================== -func (c *ComponentManager[T]) EachComponentParallel(batchSize int, yield func(*T, int) bool) { +func (c *ComponentManager[T]) EachComponentParallel(numWorkers int, yield func(*T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllDataParallel(batchSize, yield) + c.components.AllDataParallel(numWorkers, yield) } -func (c *ComponentManager[T]) EachEntityParallel(batchSize int, yield func(Entity, int) bool) { +func (c *ComponentManager[T]) EachEntityParallel(numWorkers int, yield func(Entity, int) bool) { c.assertBegin() defer c.assertEnd() - c.entities.AllDataValueParallel(batchSize, yield) + c.entities.AllDataValueParallel(numWorkers, yield) } -func (c *ComponentManager[T]) EachParallel(yield func(Entity, *T) bool) { +func (c *ComponentManager[T]) EachParallel(numWorkers int, yield func(Entity, *T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllParallel(func(i int, t *T) bool { + c.components.AllParallel(numWorkers, func(i int, t *T, workerId int) bool { entity := c.entities.Get(i) entId := *entity - shouldContinue := yield(entId, t) + shouldContinue := yield(entId, t, workerId) return shouldContinue }) } diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index d8cd1381..c05c91af 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -7,7 +7,6 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs import ( - "runtime" "sync" "github.com/negrel/assert" @@ -17,12 +16,10 @@ type PagedArray[T any] struct { book []ArrayPage[T] currentPageIndex int len int - maxNumWorkers int } func NewPagedArray[T any]() (a PagedArray[T]) { a.book = make([]ArrayPage[T], 2, initialBookSize) - a.maxNumWorkers = max(runtime.NumCPU()-2, 1) // Recover if less than 2 cpu return a } @@ -189,43 +186,28 @@ func (a *PagedArray[T]) All(yield func(int, *T) bool) { } } -func (a *PagedArray[T]) AllParallel(yield func(int, *T) bool) { - var page *ArrayPage[T] - var data *[pageSize]T - var index_offset int - - book := a.book - wg := new(sync.WaitGroup) - gorutineBudget := a.maxNumWorkers - - runner := func(data *[pageSize]T, offset int, startIndex int, wg *sync.WaitGroup) { - defer wg.Done() - for j := startIndex; j >= 0; j-- { - if !yield(offset+j, &(data[j])) { - return - } - } - } - - if a.len == 0 { - return - } - - wg.Add(int(a.currentPageIndex) + 1) - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - data = &page.data - index_offset = int(i) << pageSizeShift +func (a *PagedArray[T]) AllParallel(numWorkers int, yield func(int, *T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers + var wg sync.WaitGroup - if gorutineBudget > 0 { - go runner(data, index_offset, page.len-1, wg) - gorutineBudget-- - continue + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } - - runner(data, index_offset, page.len-1, wg) + go func(start int, end int) { + defer wg.Done() + r := end - start + for i := range r { + if !yield(i, a.Get(i+startIndex), workedId) { + return + } + } + }(startIndex, endIndex) } - wg.Wait() } @@ -269,11 +251,9 @@ func (a *PagedArray[T]) AllDataValue(yield func(T) bool) { } } -func (a *PagedArray[T]) AllDataValueParallel(batchSize int, yield func(T, int) bool) { - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) - chunkSize := a.len / numWorkers - +func (a *PagedArray[T]) AllDataValueParallel(numWorkers int, yield func(T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers var wg sync.WaitGroup wg.Add(numWorkers) @@ -296,11 +276,9 @@ func (a *PagedArray[T]) AllDataValueParallel(batchSize int, yield func(T, int) b wg.Wait() } -func (a *PagedArray[T]) AllDataParallel(batchSize int, yield func(*T, int) bool) { - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) - chunkSize := a.len / numWorkers - +func (a *PagedArray[T]) AllDataParallel(numWorkers int, yield func(*T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers var wg sync.WaitGroup wg.Add(numWorkers) diff --git a/pkg/ecs/slice.go b/pkg/ecs/slice.go index 1baad235..5567bfb7 100644 --- a/pkg/ecs/slice.go +++ b/pkg/ecs/slice.go @@ -7,23 +7,18 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs import ( - "runtime" "sync" "github.com/negrel/assert" ) type Slice[T any] struct { - data []T - len int - maxNumWorkers int - wg sync.WaitGroup + data []T + len int } func NewSlice[T any](size int) (a Slice[T]) { a.data = make([]T, 0, size) - a.maxNumWorkers = max(runtime.NumCPU()-2, 1) // Recover if less than 2 cpu - return a } @@ -139,12 +134,12 @@ func (a *Slice[T]) EachDataValue(yield func(T) bool) { } } -func (a *Slice[T]) EachParallel(batchSize int, yield func(int, *T, int) bool) { - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) - chunkSize := a.len / numWorkers +func (a *Slice[T]) EachParallel(numWorkers int, yield func(int, *T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers + var wg sync.WaitGroup - a.wg.Add(numWorkers) + wg.Add(numWorkers) for workedId := 0; workedId < numWorkers; workedId++ { startIndex := workedId * chunkSize endIndex := startIndex + chunkSize - 1 @@ -152,7 +147,7 @@ func (a *Slice[T]) EachParallel(batchSize int, yield func(int, *T, int) bool) { endIndex = a.len } go func(start int, end int) { - defer a.wg.Done() + defer wg.Done() for i := range a.data[start:end] { if !yield(i, &a.data[i+startIndex], workedId) { return @@ -160,15 +155,15 @@ func (a *Slice[T]) EachParallel(batchSize int, yield func(int, *T, int) bool) { } }(startIndex, endIndex) } - a.wg.Wait() + wg.Wait() } -func (a *Slice[T]) EachDataValueParallel(batchSize int, yield func(T, int) bool) { - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) - chunkSize := a.len / numWorkers +func (a *Slice[T]) EachDataValueParallel(numWorkers int, yield func(T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers + var wg sync.WaitGroup - a.wg.Add(numWorkers) + wg.Add(numWorkers) for workedId := 0; workedId < numWorkers; workedId++ { startIndex := workedId * chunkSize endIndex := startIndex + chunkSize - 1 @@ -176,7 +171,7 @@ func (a *Slice[T]) EachDataValueParallel(batchSize int, yield func(T, int) bool) endIndex = a.len } go func(start int, end int) { - defer a.wg.Done() + defer wg.Done() for i := range a.data[start:end] { if !yield(a.data[i+startIndex], workedId) { return @@ -184,15 +179,15 @@ func (a *Slice[T]) EachDataValueParallel(batchSize int, yield func(T, int) bool) } }(startIndex, endIndex) } - a.wg.Wait() + wg.Wait() } -func (a *Slice[T]) EachDataParallel(batchSize int, yield func(*T, int) bool) { - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(a.len/batchSize, a.maxNumWorkers), 1) - chunkSize := a.len / numWorkers +func (a *Slice[T]) EachDataParallel(numWorkers int, yield func(*T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers + var wg sync.WaitGroup - a.wg.Add(numWorkers) + wg.Add(numWorkers) for workedId := 0; workedId < numWorkers; workedId++ { startIndex := workedId * chunkSize endIndex := startIndex + chunkSize - 1 @@ -200,7 +195,7 @@ func (a *Slice[T]) EachDataParallel(batchSize int, yield func(*T, int) bool) { endIndex = a.len } go func(start int, end int) { - defer a.wg.Done() + defer wg.Done() for i := range a.data[start:end] { if !yield(&a.data[i+startIndex], workedId) { return @@ -208,5 +203,5 @@ func (a *Slice[T]) EachDataParallel(batchSize int, yield func(*T, int) bool) { } }(startIndex, endIndex) } - a.wg.Wait() + wg.Wait() } diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 0bdfe84c..f88a2e7f 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -49,10 +49,12 @@ type CollisionDetectionSystem struct { CollisionChunkComponentManager *stdcomponents.CollisionChunkComponentManager gridLookup map[stdcomponents.CollisionLayer]ecs.Entity + numWorkers int } func (s *CollisionDetectionSystem) Init() { s.gridLookup = make(map[stdcomponents.CollisionLayer]ecs.Entity) + s.numWorkers = runtime.NumCPU() - 2 } func (s *CollisionDetectionSystem) Run(dt time.Duration) { s.setup() @@ -60,10 +62,8 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { } func (s *CollisionDetectionSystem) Destroy() {} func (s *CollisionDetectionSystem) setup() { - const batchSize = 1 << 14 - // Reset grids - s.CollisionGridComponentManager.EachEntityParallel(batchSize, func(entity ecs.Entity, workerId int) bool { + s.CollisionGridComponentManager.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { grid := s.CollisionGridComponentManager.GetUnsafe(entity) assert.NotNil(grid) grid.Entities.Reset() @@ -75,8 +75,8 @@ func (s *CollisionDetectionSystem) setup() { }) // Accumulate used CollisionLayers - var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, runtime.NumCPU()-2) - s.GenericCollider.EachEntityParallel(batchSize*4, func(entity ecs.Entity, workerId int) bool { + var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, s.numWorkers) + s.GenericCollider.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { collider := s.GenericCollider.GetUnsafe(entity) assert.NotNil(collider) collisionLayerAccumulators[workerId] |= 1 << collider.Layer @@ -172,12 +172,12 @@ func (s *CollisionDetectionSystem) setup() { Codes: ecs.NewSlice[uint64](64), Components: ecs.NewSlice[stdcomponents.BvhComponent](64), }) - const colorbase int = 70 + const colorbase int = 120 s.Tints.Create(chunkEntity, color.RGBA{ R: uint8(colorbase + rand.Intn(255-colorbase)), G: uint8(colorbase + rand.Intn(255-colorbase)), B: uint8(colorbase + rand.Intn(255-colorbase)), - A: 50, + A: 70, }) } @@ -194,7 +194,7 @@ func (s *CollisionDetectionSystem) setup() { return true }) - s.CollisionChunkComponentManager.EachEntityParallel(batchSize, func(chunkEntity ecs.Entity, workerId int) bool { + s.CollisionChunkComponentManager.EachEntityParallel(s.numWorkers, func(chunkEntity ecs.Entity, workerId int) bool { tree := s.BvhTreeComponentManager.GetUnsafe(chunkEntity) assert.NotNil(tree) tree.Build() diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index 591fff44..4f9d02d1 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -13,6 +13,7 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "math" + "runtime" "slices" "sync" "time" @@ -38,6 +39,7 @@ type Render2DCamerasSystem struct { Rotations *stdcomponents.RotationComponentManager renderObjects []renderObject renderObjectsSorted []renderObjectSorted + numWorkers int } type renderObject struct { @@ -53,6 +55,7 @@ type renderObjectSorted struct { func (s *Render2DCamerasSystem) Init() { s.renderObjects = make([]renderObject, 0, s.RenderVisibles.Len()) + s.numWorkers = runtime.NumCPU() - 2 } func (s *Render2DCamerasSystem) Run(dt time.Duration) { @@ -150,7 +153,7 @@ func (s *Render2DCamerasSystem) prepareRender(dt time.Duration) { func (s *Render2DCamerasSystem) prepareAnimations(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) animation := s.AnimationPlayers.GetUnsafe(entity) if animation == nil { @@ -168,7 +171,7 @@ func (s *Render2DCamerasSystem) prepareAnimations(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) flipped := s.Flips.GetUnsafe(entity) if flipped == nil { @@ -187,7 +190,7 @@ func (s *Render2DCamerasSystem) prepareFlips(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() //dts := dt.Seconds() - s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) position := s.Positions.GetUnsafe(entity) if position == nil { @@ -205,7 +208,7 @@ func (s *Render2DCamerasSystem) preparePositions(wg *sync.WaitGroup, dt time.Dur func (s *Render2DCamerasSystem) prepareRotations(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) rotation := s.Rotations.GetUnsafe(entity) if rotation == nil { @@ -218,7 +221,7 @@ func (s *Render2DCamerasSystem) prepareRotations(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) prepareScales(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) if scale == nil { @@ -232,7 +235,7 @@ func (s *Render2DCamerasSystem) prepareScales(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) prepareTints(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(128, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { tr := s.Textures.GetUnsafe(entity) tint := s.Tints.GetUnsafe(entity) if tint == nil { diff --git a/stdsystems/texture-position-smooth.go b/stdsystems/texture-position-smooth.go index d5ce8e65..bf1bae49 100644 --- a/stdsystems/texture-position-smooth.go +++ b/stdsystems/texture-position-smooth.go @@ -13,6 +13,7 @@ import ( "gomp/stdcomponents" "gomp/vectors" "math" + "runtime" "time" ) @@ -24,34 +25,36 @@ type TexturePositionSmoothSystem struct { TexturePositionSmooth *stdcomponents.TexturePositionSmoothComponentManager Position *stdcomponents.PositionComponentManager RLTexture *stdcomponents.RLTextureProComponentManager + numWorkers int } func (s *TexturePositionSmoothSystem) Init() { + s.numWorkers = runtime.NumCPU() - 2 } func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { //DEBUG Temporary, TODO: remove if rl.IsKeyPressed(rl.KeyI) { - s.TexturePositionSmooth.EachComponentParallel(128, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.numWorkers, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothOff return true }) } if rl.IsKeyPressed(rl.KeyO) { - s.TexturePositionSmooth.EachComponentParallel(128, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.numWorkers, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothLerp return true }) } if rl.IsKeyPressed(rl.KeyP) { - s.TexturePositionSmooth.EachComponentParallel(128, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.numWorkers, func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothExpDecay return true }) } //END DEBUG - s.TexturePositionSmooth.EachEntityParallel(128, func(entity ecs.Entity, i int) bool { + s.TexturePositionSmooth.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, i int) bool { position := s.Position.GetUnsafe(entity) texture := s.RLTexture.GetUnsafe(entity) smooth := s.TexturePositionSmooth.GetUnsafe(entity) From 248bfb8274d8bb756c0ff4f21373064fdce535be Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 14 Apr 2025 14:35:05 +0300 Subject: [PATCH 136/196] breaking changes paged-array.go rename iters --- pkg/ecs/component-manager-shared.go | 12 ++++++------ pkg/ecs/component-manager.go | 14 +++++++------- pkg/ecs/paged-array.go | 12 ++++++------ stdsystems/collision-detection-bvh.go | 2 +- stdsystems/collision-detection.go | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 00d8dbde..65c84559 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -218,19 +218,19 @@ func (c *SharedComponentManager[T]) Clean() { func (c *SharedComponentManager[T]) EachComponent(yield func(*T) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllData(yield) + c.components.EachData(yield) } func (c *SharedComponentManager[T]) EachEntity(yield func(Entity) bool) { c.assertBegin() defer c.assertEnd() - c.entities.AllDataValue(yield) + c.entities.EachDataValue(yield) } func (c *SharedComponentManager[T]) Each(yield func(Entity, *T) bool) { c.assertBegin() defer c.assertEnd() - c.components.All(func(i int, d *T) bool { + c.components.Each(func(i int, d *T) bool { entity := c.entities.Get(i) entId := *entity shouldContinue := yield(entId, d) @@ -245,19 +245,19 @@ func (c *SharedComponentManager[T]) Each(yield func(Entity, *T) bool) { func (c *SharedComponentManager[T]) EachComponentParallel(numWorkers int, yield func(*T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllDataParallel(numWorkers, yield) + c.components.EachDataParallel(numWorkers, yield) } func (c *SharedComponentManager[T]) EachEntityParallel(numWorkers int, yield func(Entity, int) bool) { c.assertBegin() defer c.assertEnd() - c.entities.AllDataValueParallel(numWorkers, yield) + c.entities.EachDataValueParallel(numWorkers, yield) } func (c *SharedComponentManager[T]) EachParallel(numWorkers int, yield func(Entity, *T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllParallel(numWorkers, func(i int, t *T, workerId int) bool { + c.components.EachParallel(numWorkers, func(i int, t *T, workerId int) bool { entity := c.entities.Get(i) entId := *entity shouldContinue := yield(entId, t, workerId) diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index cff5d0a4..4dec5e1e 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -236,19 +236,19 @@ func (c *ComponentManager[T]) Clean() { func (c *ComponentManager[T]) EachComponent(yield func(*T) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllData(yield) + c.components.EachData(yield) } func (c *ComponentManager[T]) EachEntity(yield func(Entity) bool) { c.assertBegin() defer c.assertEnd() - c.entities.AllDataValue(yield) + c.entities.EachDataValue(yield) } func (c *ComponentManager[T]) Each(yield func(Entity, *T) bool) { c.assertBegin() defer c.assertEnd() - c.components.All(func(i int, d *T) bool { + c.components.Each(func(i int, d *T) bool { entity := c.entities.Get(i) entId := *entity shouldContinue := yield(entId, d) @@ -263,19 +263,19 @@ func (c *ComponentManager[T]) Each(yield func(Entity, *T) bool) { func (c *ComponentManager[T]) EachComponentParallel(numWorkers int, yield func(*T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllDataParallel(numWorkers, yield) + c.components.EachDataParallel(numWorkers, yield) } func (c *ComponentManager[T]) EachEntityParallel(numWorkers int, yield func(Entity, int) bool) { c.assertBegin() defer c.assertEnd() - c.entities.AllDataValueParallel(numWorkers, yield) + c.entities.EachDataValueParallel(numWorkers, yield) } func (c *ComponentManager[T]) EachParallel(numWorkers int, yield func(Entity, *T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.AllParallel(numWorkers, func(i int, t *T, workerId int) bool { + c.components.EachParallel(numWorkers, func(i int, t *T, workerId int) bool { entity := c.entities.Get(i) entId := *entity shouldContinue := yield(entId, t, workerId) @@ -342,7 +342,7 @@ func (c *ComponentManager[T]) getChangesBinary(source *PagedArray[Entity]) Compo components := make([]T, 0, changesLen) entities := make([]Entity, 0, changesLen) - source.AllData(func(e *Entity) bool { + source.EachData(func(e *Entity) bool { assert.True(e != nil) entId := *e assert.True(c.Has(entId)) diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index c05c91af..97c3c23f 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -164,7 +164,7 @@ func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { return pageId, index } -func (a *PagedArray[T]) All(yield func(int, *T) bool) { +func (a *PagedArray[T]) Each(yield func(int, *T) bool) { var page *ArrayPage[T] var index_offset int @@ -186,7 +186,7 @@ func (a *PagedArray[T]) All(yield func(int, *T) bool) { } } -func (a *PagedArray[T]) AllParallel(numWorkers int, yield func(int, *T, int) bool) { +func (a *PagedArray[T]) EachParallel(numWorkers int, yield func(int, *T, int) bool) { assert.True(numWorkers > 0) var chunkSize = a.len / numWorkers var wg sync.WaitGroup @@ -211,7 +211,7 @@ func (a *PagedArray[T]) AllParallel(numWorkers int, yield func(int, *T, int) boo wg.Wait() } -func (a *PagedArray[T]) AllData(yield func(*T) bool) { +func (a *PagedArray[T]) EachData(yield func(*T) bool) { var page *ArrayPage[T] book := a.book @@ -231,7 +231,7 @@ func (a *PagedArray[T]) AllData(yield func(*T) bool) { } } -func (a *PagedArray[T]) AllDataValue(yield func(T) bool) { +func (a *PagedArray[T]) EachDataValue(yield func(T) bool) { var page *ArrayPage[T] book := a.book @@ -251,7 +251,7 @@ func (a *PagedArray[T]) AllDataValue(yield func(T) bool) { } } -func (a *PagedArray[T]) AllDataValueParallel(numWorkers int, yield func(T, int) bool) { +func (a *PagedArray[T]) EachDataValueParallel(numWorkers int, yield func(T, int) bool) { assert.True(numWorkers > 0) var chunkSize = a.len / numWorkers var wg sync.WaitGroup @@ -276,7 +276,7 @@ func (a *PagedArray[T]) AllDataValueParallel(numWorkers int, yield func(T, int) wg.Wait() } -func (a *PagedArray[T]) AllDataParallel(numWorkers int, yield func(*T, int) bool) { +func (a *PagedArray[T]) EachDataParallel(numWorkers int, yield func(*T, int) bool) { assert.True(numWorkers > 0) var chunkSize = a.len / numWorkers var wg sync.WaitGroup diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index bd855931..d375f408 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -151,7 +151,7 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity func (s *CollisionDetectionBVHSystem) registerCollisionEvents() { for i := range s.collisionEvents { events := &s.collisionEvents[i] - events.AllData(func(event *CollisionEvent) bool { + events.EachData(func(event *CollisionEvent) bool { pair := CollisionPair{event.entityA, event.entityB} s.currentCollisions[pair] = struct{}{} displacement := event.normal.Scale(event.depth) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index f88a2e7f..c734806d 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -147,7 +147,7 @@ func (s *CollisionDetectionSystem) setup() { grid.ChunkSize = newChunkSize } - grid.Entities.AllDataValue(func(entity ecs.Entity) bool { + grid.Entities.EachDataValue(func(entity ecs.Entity) bool { position := s.Positions.GetUnsafe(entity) assert.NotNil(position) From b3a64796706869dfccb27b47520bbbe12bd91737 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 14 Apr 2025 18:35:41 +0300 Subject: [PATCH 137/196] breaking changes - refactor iterators --- examples/new-api/game.go | 4 +- examples/new-api/systems/asterodd.go | 6 +- examples/new-api/systems/audio.go | 2 +- examples/new-api/systems/camera-main.go | 2 +- examples/new-api/systems/camera-minimap.go | 2 +- examples/new-api/systems/collision-handler.go | 2 +- examples/new-api/systems/damping.go | 2 +- examples/new-api/systems/debug-info.go | 8 +- examples/new-api/systems/hp.go | 4 +- examples/new-api/systems/player.go | 2 +- examples/new-api/systems/render-bogdan.go | 18 +- examples/new-api/systems/render-overlay.go | 14 +- examples/new-api/systems/space-spawner.go | 2 +- examples/new-api/systems/spaceship-intents.go | 2 +- examples/new-api/systems/spatial-audio.go | 4 +- examples/new-api/systems/texture-circle.go | 5 +- examples/new-api/systems/texture-rect.go | 5 +- pkg/ecs/component-manager-shared.go | 48 ++-- pkg/ecs/component-manager.go | 50 +++-- pkg/ecs/paged-array.go | 210 +++++++++--------- pkg/ecs/slice.go | 196 +++++++++------- stdsystems/animation-player.go | 53 ++--- stdsystems/animation-spritematrix.go | 2 +- stdsystems/collider.go | 6 +- stdsystems/collision-detection-bvh.go | 4 +- stdsystems/collision-detection-grid.go | 4 +- stdsystems/collision-detection.go | 12 +- stdsystems/collision-reslution.go | 2 +- stdsystems/culling.go | 11 +- stdsystems/render-2d-cameras.go | 16 +- stdsystems/render.go | 2 +- stdsystems/sprite-matrix.go | 2 +- stdsystems/sprite.go | 2 +- stdsystems/texture-position-smooth.go | 8 +- stdsystems/velocity.go | 2 +- stdsystems/ysort.go | 2 +- taskfile.yml | 2 +- 37 files changed, 383 insertions(+), 335 deletions(-) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 798bf585..e5f19a71 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -103,8 +103,8 @@ func (g *Game) FixedUpdate(dt time.Duration) { systems.Velocity.Run(dt) systems.ColliderSystem.Run(dt) - systems.CollisionDetection.Run(dt) - //systems.CollisionDetectionBVH.Run(dt) + //systems.CollisionDetection.Run(dt) + systems.CollisionDetectionBVH.Run(dt) systems.CollisionResolution.Run(dt) scene.FixedUpdate(dt) diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index eb0eb690..015a07b0 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -161,7 +161,7 @@ func (s *AssteroddSystem) Init() { s.SceneManager.Create(manager, components.AsteroidSceneManager{}) } func (s *AssteroddSystem) Run(dt time.Duration) { - s.PlayerTags.EachEntity(func(e ecs.Entity) bool { + s.PlayerTags.EachEntity()(func(e ecs.Entity) bool { intents := s.SpaceshipIntents.GetUnsafe(e) intents.MoveUp = false @@ -191,9 +191,9 @@ func (s *AssteroddSystem) Run(dt time.Duration) { return true }) - s.SceneManager.EachEntity(func(e ecs.Entity) bool { + s.SceneManager.EachEntity()(func(e ecs.Entity) bool { sceneManager := s.SceneManager.GetUnsafe(e) - s.PlayerTags.EachEntity(func(e ecs.Entity) bool { + s.PlayerTags.EachEntity()(func(e ecs.Entity) bool { playerHp := s.Hps.GetUnsafe(e) if playerHp == nil { return true diff --git a/examples/new-api/systems/audio.go b/examples/new-api/systems/audio.go index 169aa375..70d67641 100644 --- a/examples/new-api/systems/audio.go +++ b/examples/new-api/systems/audio.go @@ -36,7 +36,7 @@ func (s *AudioSystem) Init() { } func (s *AudioSystem) Run(dt time.Duration) { - s.SoundEffects.EachEntity(func(entity ecs.Entity) bool { + s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { soundEffect := s.SoundEffects.GetUnsafe(entity) clip := soundEffect.Clip diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index a8388c13..a33fc086 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -86,7 +86,7 @@ func (s *MainCameraSystem) Run(dt time.Duration) { if rl.IsKeyPressed(rl.KeyR) { s.shouldRotate = !s.shouldRotate } - s.Player.EachEntity(func(entity ecs.Entity) bool { + s.Player.EachEntity()(func(entity ecs.Entity) bool { playerPosition := s.Position.GetUnsafe(entity) c := s.Cameras.GetUnsafe(s.mainCamera) //decay := 40.0 // DECAY IS TICKRATE DEPENDENT diff --git a/examples/new-api/systems/camera-minimap.go b/examples/new-api/systems/camera-minimap.go index f6c2007c..55e3b432 100644 --- a/examples/new-api/systems/camera-minimap.go +++ b/examples/new-api/systems/camera-minimap.go @@ -81,7 +81,7 @@ func (s *MinimapSystem) Run(dt time.Duration) bool { if s.disabled { return false } - s.Player.EachEntity(func(entity ecs.Entity) bool { + s.Player.EachEntity()(func(entity ecs.Entity) bool { playerPosition := s.Position.GetUnsafe(entity) rotation := s.Rotation.GetUnsafe(entity) diff --git a/examples/new-api/systems/collision-handler.go b/examples/new-api/systems/collision-handler.go index 19fd1507..60c4768f 100644 --- a/examples/new-api/systems/collision-handler.go +++ b/examples/new-api/systems/collision-handler.go @@ -49,7 +49,7 @@ type CollisionHandlerSystem struct { func (s *CollisionHandlerSystem) Init() {} func (s *CollisionHandlerSystem) Run(dt time.Duration) { - s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { + s.Collisions.EachComponent()(func(collision *stdcomponents.Collision) bool { switch collision.State { case stdcomponents.CollisionStateEnter: if s.checkBulletCollisionEnter(collision.E1, collision.E2) { diff --git a/examples/new-api/systems/damping.go b/examples/new-api/systems/damping.go index 05ef492e..40586591 100644 --- a/examples/new-api/systems/damping.go +++ b/examples/new-api/systems/damping.go @@ -27,7 +27,7 @@ func (s *DampingSystem) Init() {} func (s *DampingSystem) Run(dt time.Duration) { dampingFactor := float32(0.98) // Damping factor for velocity - s.Velocities.EachEntity(func(e ecs.Entity) bool { + s.Velocities.EachEntity()(func(e ecs.Entity) bool { velocity := s.Velocities.GetUnsafe(e) rigidbody := s.RigidBodies.GetUnsafe(e) diff --git a/examples/new-api/systems/debug-info.go b/examples/new-api/systems/debug-info.go index 126ab105..b2651356 100644 --- a/examples/new-api/systems/debug-info.go +++ b/examples/new-api/systems/debug-info.go @@ -60,7 +60,7 @@ func (s *DebugInfoSystem) Init() { func (s *DebugInfoSystem) Run(dt time.Duration) bool { if rl.IsKeyPressed(rl.KeyF6) { if !s.debug { - s.BoxColliders.EachEntity(func(e ecs.Entity) bool { + s.BoxColliders.EachEntity()(func(e ecs.Entity) bool { col := s.BoxColliders.GetUnsafe(e) scale := s.Scales.GetUnsafe(e) position := s.Positions.GetUnsafe(e) @@ -76,7 +76,7 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { }, float32(rotation.Degrees()), rl.DarkGreen, e) return true }) - s.CircleColliders.EachEntity(func(e ecs.Entity) bool { + s.CircleColliders.EachEntity()(func(e ecs.Entity) bool { col := s.CircleColliders.GetUnsafe(e) scale := s.Scales.GetUnsafe(e) pos := s.Positions.GetUnsafe(e) @@ -103,7 +103,7 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { // TODO: Parallelize this with future batches feature // Follow child to texture of parent box collider - s.BoxColliders.EachEntity(func(e ecs.Entity) bool { + s.BoxColliders.EachEntity()(func(e ecs.Entity) bool { parentAABB := s.AABBs.GetUnsafe(e) parentPosition := s.Positions.GetUnsafe(e) col := s.BoxColliders.GetUnsafe(e) @@ -145,7 +145,7 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { }) // TODO: Parallelize this with future batches feature // Follow child to texture of parent circle collider - s.CircleColliders.EachEntity(func(e ecs.Entity) bool { + s.CircleColliders.EachEntity()(func(e ecs.Entity) bool { parentAABB := s.AABBs.GetUnsafe(e) pos := s.Positions.GetUnsafe(e) col := s.CircleColliders.GetUnsafe(e) diff --git a/examples/new-api/systems/hp.go b/examples/new-api/systems/hp.go index b6907ccb..a6fe547e 100644 --- a/examples/new-api/systems/hp.go +++ b/examples/new-api/systems/hp.go @@ -35,13 +35,13 @@ type HpSystem struct { func (s *HpSystem) Init() {} func (s *HpSystem) Run(dt time.Duration) { - s.Hps.EachEntity(func(e ecs.Entity) bool { + s.Hps.EachEntity()(func(e ecs.Entity) bool { hp := s.Hps.GetUnsafe(e) if hp.Hp <= 0 { asteroid := s.Asteroids.GetUnsafe(e) player := s.Players.GetUnsafe(e) - s.AsteroidSceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { + s.AsteroidSceneManager.EachComponent()(func(a *components.AsteroidSceneManager) bool { if asteroid != nil { a.PlayerScore += hp.MaxHp } diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 6fc82d7e..9c8875a7 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -75,7 +75,7 @@ func (s *PlayerSystem) Run() { var speed float32 = 300 - for e := range s.Controllers.EachEntity { + for e := range s.Controllers.EachEntity() { velocity := s.Velocities.GetUnsafe(e) flip := s.Flips.GetUnsafe(e) animationState := s.AnimationStates.GetUnsafe(e) diff --git a/examples/new-api/systems/render-bogdan.go b/examples/new-api/systems/render-bogdan.go index 38cd1389..591cf93b 100644 --- a/examples/new-api/systems/render-bogdan.go +++ b/examples/new-api/systems/render-bogdan.go @@ -81,14 +81,14 @@ func (s *RenderBogdanSystem) Run(dt time.Duration) bool { rl.DrawLine(0, i*gridSize, 1024, i*gridSize, rl.Green) } s.render() - s.ColliderBoxes.EachEntity(func(e ecs.Entity) bool { + s.ColliderBoxes.EachEntity()(func(e ecs.Entity) bool { box := s.ColliderBoxes.GetUnsafe(e) pos := s.Positions.GetUnsafe(e) rl.DrawRectangleLines(int32(pos.XY.X), int32(pos.XY.Y), int32(box.WH.X), int32(box.WH.Y), rl.Red) return true }) - s.Collisions.EachEntity(func(entity ecs.Entity) bool { + s.Collisions.EachEntity()(func(entity ecs.Entity) bool { pos := s.Positions.GetUnsafe(entity) rl.DrawRectangle(int32(pos.XY.X), int32(pos.XY.X), 16, 16, rl.Red) return true @@ -108,7 +108,7 @@ func (s *RenderBogdanSystem) render() { if cap(s.renderList) < s.Renderables.Len() { s.renderList = append(s.renderList, make([]renderEntry, 0, s.Renderables.Len()-cap(s.renderList))...) } - s.Renderables.EachEntity(func(e ecs.Entity) bool { + s.Renderables.EachEntity()(func(e ecs.Entity) bool { sprite := s.SpriteMatrixes.Get(e) renderOrder := s.RenderOrders.GetUnsafe(e) s.renderList = append(s.renderList, renderEntry{ @@ -162,7 +162,7 @@ func (s *RenderBogdanSystem) prepareRender(dt time.Duration) { func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) animation := s.AnimationPlayers.GetUnsafe(entity) if animation == nil { @@ -180,7 +180,7 @@ func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) mirrored := s.Flips.GetUnsafe(entity) if mirrored == nil { @@ -199,7 +199,7 @@ func (s *RenderBogdanSystem) prepareFlips(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() //dts := dt.Seconds() - s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) position := s.Positions.GetUnsafe(entity) if position == nil { @@ -217,7 +217,7 @@ func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Durati func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) rotation := s.Rotations.GetUnsafe(entity) if rotation == nil { @@ -230,7 +230,7 @@ func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) if scale == nil { @@ -244,7 +244,7 @@ func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareTints(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { tr := s.RlTexturePros.GetUnsafe(entity) tint := s.Tints.GetUnsafe(entity) if tint == nil { diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index d298f0d6..b17b2f65 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -87,7 +87,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } } - s.Cameras.EachEntity(func(entity ecs.Entity) bool { + s.Cameras.EachEntity()(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) frame := s.FrameBuffer2D.GetUnsafe(entity) switch frame.Layer { @@ -101,7 +101,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { rl.BeginMode2D(camera.Camera2D) cameraRect := camera.Rect() - s.CollisionChunks.EachEntity(func(e ecs.Entity) bool { + s.CollisionChunks.EachEntity()(func(e ecs.Entity) bool { chunk := s.CollisionChunks.GetUnsafe(e) assert.NotNil(chunk) @@ -118,7 +118,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { tree := s.BvhTrees.GetUnsafe(e) assert.NotNil(tree) - tree.AabbNodes.EachData(func(a *stdcomponents.AABB) bool { + tree.AabbNodes.EachData()(func(a *stdcomponents.AABB) bool { // Simple AABB culling if s.intersects(cameraRect, a.Rect()) { rl.DrawRectangleRec(rl.Rectangle{ @@ -149,7 +149,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } return true }) - s.AABBs.EachEntity(func(e ecs.Entity) bool { + s.AABBs.EachEntity()(func(e ecs.Entity) bool { aabb := s.AABBs.GetUnsafe(e) clr := rl.Green isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(e) @@ -166,12 +166,12 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } return true }) - s.Collisions.EachEntity(func(entity ecs.Entity) bool { + s.Collisions.EachEntity()(func(entity ecs.Entity) bool { pos := s.Positions.GetUnsafe(entity) rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) return true }) - s.Textures.EachComponent(func(r *stdcomponents.RLTexturePro) bool { + s.Textures.EachComponent()(func(r *stdcomponents.RLTexturePro) bool { rl.DrawRectanglePro(rl.Rectangle{ X: r.Dest.X - 2, Y: r.Dest.Y - 2, @@ -189,7 +189,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 70, 20, rl.RayWhite) rl.DrawText(fmt.Sprintf("%d debugLvl", s.debugLvl), 10, 90, 20, rl.RayWhite) // Game over - s.SceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { + s.SceneManager.EachComponent()(func(a *components.AsteroidSceneManager) bool { rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), 10, 30, 20, rl.RayWhite) rl.DrawText(fmt.Sprintf("Score: %d", a.PlayerScore), 10, 50, 20, rl.RayWhite) if a.PlayerHp <= 0 { diff --git a/examples/new-api/systems/space-spawner.go b/examples/new-api/systems/space-spawner.go index 36163858..9d63916d 100644 --- a/examples/new-api/systems/space-spawner.go +++ b/examples/new-api/systems/space-spawner.go @@ -47,7 +47,7 @@ type SpaceSpawnerSystem struct { func (s *SpaceSpawnerSystem) Init() {} func (s *SpaceSpawnerSystem) Run(dt time.Duration) { - s.SpaceSpawners.EachEntity(func(e ecs.Entity) bool { + s.SpaceSpawners.EachEntity()(func(e ecs.Entity) bool { position := s.Positions.GetUnsafe(e) velocity := s.Velocities.GetUnsafe(e) diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index c8ca1b24..fe8d22df 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -62,7 +62,7 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - s.SpaceshipIntents.EachEntity(func(entity ecs.Entity) bool { + s.SpaceshipIntents.EachEntity()(func(entity ecs.Entity) bool { intent := s.SpaceshipIntents.GetUnsafe(entity) vel := s.Velocities.GetUnsafe(entity) rot := s.Rotations.GetUnsafe(entity) diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go index 9f54e5bb..be65a4ea 100644 --- a/examples/new-api/systems/spatial-audio.go +++ b/examples/new-api/systems/spatial-audio.go @@ -41,7 +41,7 @@ func (s *SpatialAudioSystem) Init() { func (s *SpatialAudioSystem) Run(dt time.Duration) { var player ecs.Entity = 0 - s.Player.EachEntity(func(entity ecs.Entity) bool { + s.Player.EachEntity()(func(entity ecs.Entity) bool { player = entity return false }) @@ -56,7 +56,7 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { return } - s.SoundEffects.EachEntity(func(entity ecs.Entity) bool { + s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { soundEffect := s.SoundEffects.GetUnsafe(entity) clip := soundEffect.Clip diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go index fcf20454..2c9ccf3c 100644 --- a/examples/new-api/systems/texture-circle.go +++ b/examples/new-api/systems/texture-circle.go @@ -39,10 +39,11 @@ func (s *TextureCircleSystem) Init() { } func (s *TextureCircleSystem) Run(dt time.Duration) { - s.Circles.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, i int) bool { + s.Circles.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, i int) bool { circle := s.Circles.GetUnsafe(entity) + assert.NotNil(circle, "circle is nil; entity: %d", entity) texture := s.Textures.GetUnsafe(entity) - assert.Nil(texture, "texture is nil; entity: %d", entity) + assert.NotNil(texture, "texture is nil; entity: %d", entity) texture.Texture = &s.texture.Texture texture.Dest.X = circle.CenterX diff --git a/examples/new-api/systems/texture-rect.go b/examples/new-api/systems/texture-rect.go index 2ceb1d33..4de502df 100644 --- a/examples/new-api/systems/texture-rect.go +++ b/examples/new-api/systems/texture-rect.go @@ -38,10 +38,11 @@ func (s *TextureRectSystem) Init() { func (s *TextureRectSystem) Run(dt time.Duration) { // Create shallow copy of texture to draw rectangles - s.TextureRect.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, i int) bool { + s.TextureRect.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, i int) bool { rect := s.TextureRect.GetUnsafe(entity) + assert.NotNil(rect, "rect is nil; entity: %d", entity) texture := s.Textures.GetUnsafe(entity) - assert.Nil(texture, "texture is nil; entity: %d", entity) + assert.NotNil(texture, "texture is nil; entity: %d", entity) texture.Texture = &s.texture.Texture texture.Dest = rect.Dest diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 65c84559..822a6311 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -215,54 +215,58 @@ func (c *SharedComponentManager[T]) Clean() { // Iterators // ======================================================== -func (c *SharedComponentManager[T]) EachComponent(yield func(*T) bool) { +func (c *SharedComponentManager[T]) EachComponent() func(yield func(*T) bool) { c.assertBegin() defer c.assertEnd() - c.components.EachData(yield) + return c.components.EachData() } -func (c *SharedComponentManager[T]) EachEntity(yield func(Entity) bool) { +func (c *SharedComponentManager[T]) EachEntity() func(yield func(Entity) bool) { c.assertBegin() defer c.assertEnd() - c.entities.EachDataValue(yield) + return c.entities.EachDataValue() } -func (c *SharedComponentManager[T]) Each(yield func(Entity, *T) bool) { +func (c *SharedComponentManager[T]) Each() func(yield func(Entity, *T) bool) { c.assertBegin() defer c.assertEnd() - c.components.Each(func(i int, d *T) bool { - entity := c.entities.Get(i) - entId := *entity - shouldContinue := yield(entId, d) - return shouldContinue - }) + return func(yield func(Entity, *T) bool) { + c.components.Each()(func(i int, d *T) bool { + entity := c.entities.Get(i) + entId := *entity + shouldContinue := yield(entId, d) + return shouldContinue + }) + } } // ======================================================== // Iterators Parallel // ======================================================== -func (c *SharedComponentManager[T]) EachComponentParallel(numWorkers int, yield func(*T, int) bool) { +func (c *SharedComponentManager[T]) EachComponentParallel(numWorkers int) func(yield func(*T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.EachDataParallel(numWorkers, yield) + return c.components.EachDataParallel(numWorkers) } -func (c *SharedComponentManager[T]) EachEntityParallel(numWorkers int, yield func(Entity, int) bool) { +func (c *SharedComponentManager[T]) EachEntityParallel(numWorkers int) func(yield func(Entity, int) bool) { c.assertBegin() defer c.assertEnd() - c.entities.EachDataValueParallel(numWorkers, yield) + return c.entities.EachDataValueParallel(numWorkers) } -func (c *SharedComponentManager[T]) EachParallel(numWorkers int, yield func(Entity, *T, int) bool) { +func (c *SharedComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entity, *T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.EachParallel(numWorkers, func(i int, t *T, workerId int) bool { - entity := c.entities.Get(i) - entId := *entity - shouldContinue := yield(entId, t, workerId) - return shouldContinue - }) + return func(yield func(Entity, *T, int) bool) { + c.components.EachParallel(numWorkers)(func(i int, t *T, workerId int) bool { + entity := c.entities.Get(i) + entId := *entity + shouldContinue := yield(entId, t, workerId) + return shouldContinue + }) + } } // ======================================================== diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 4dec5e1e..7c453e6d 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -233,54 +233,58 @@ func (c *ComponentManager[T]) Clean() { // Iterators // ======================================================== -func (c *ComponentManager[T]) EachComponent(yield func(*T) bool) { +func (c *ComponentManager[T]) EachComponent() func(yield func(entity *T) bool) { c.assertBegin() defer c.assertEnd() - c.components.EachData(yield) + return c.components.EachData() } -func (c *ComponentManager[T]) EachEntity(yield func(Entity) bool) { +func (c *ComponentManager[T]) EachEntity() func(yield func(entity Entity) bool) { c.assertBegin() defer c.assertEnd() - c.entities.EachDataValue(yield) + return c.entities.EachDataValue() } -func (c *ComponentManager[T]) Each(yield func(Entity, *T) bool) { +func (c *ComponentManager[T]) Each() func(yield func(entity Entity, component *T) bool) { c.assertBegin() defer c.assertEnd() - c.components.Each(func(i int, d *T) bool { - entity := c.entities.Get(i) - entId := *entity - shouldContinue := yield(entId, d) - return shouldContinue - }) + return func(yield func(entity Entity, component *T) bool) { + c.components.Each()(func(i int, d *T) bool { + entity := c.entities.Get(i) + entId := *entity + shouldContinue := yield(entId, d) + return shouldContinue + }) + } } // ======================================================== // Iterators Parallel // ======================================================== -func (c *ComponentManager[T]) EachComponentParallel(numWorkers int, yield func(*T, int) bool) { +func (c *ComponentManager[T]) EachComponentParallel(numWorkers int) func(yield func(*T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.EachDataParallel(numWorkers, yield) + return c.components.EachDataParallel(numWorkers) } -func (c *ComponentManager[T]) EachEntityParallel(numWorkers int, yield func(Entity, int) bool) { +func (c *ComponentManager[T]) EachEntityParallel(numWorkers int) func(yield func(Entity, int) bool) { c.assertBegin() defer c.assertEnd() - c.entities.EachDataValueParallel(numWorkers, yield) + return c.entities.EachDataValueParallel(numWorkers) } -func (c *ComponentManager[T]) EachParallel(numWorkers int, yield func(Entity, *T, int) bool) { +func (c *ComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entity, *T, int) bool) { c.assertBegin() defer c.assertEnd() - c.components.EachParallel(numWorkers, func(i int, t *T, workerId int) bool { - entity := c.entities.Get(i) - entId := *entity - shouldContinue := yield(entId, t, workerId) - return shouldContinue - }) + return func(yield func(Entity, *T, int) bool) { + c.components.EachParallel(numWorkers)(func(i int, t *T, workerId int) bool { + entity := c.entities.Get(i) + entId := *entity + shouldContinue := yield(entId, t, workerId) + return shouldContinue + }) + } } // ======================================================== @@ -342,7 +346,7 @@ func (c *ComponentManager[T]) getChangesBinary(source *PagedArray[Entity]) Compo components := make([]T, 0, changesLen) entities := make([]Entity, 0, changesLen) - source.EachData(func(e *Entity) bool { + source.EachData()(func(e *Entity) bool { assert.True(e != nil) entId := *e assert.True(c.Has(entId)) diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 97c3c23f..19857fd5 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -164,141 +164,151 @@ func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { return pageId, index } -func (a *PagedArray[T]) Each(yield func(int, *T) bool) { - var page *ArrayPage[T] - var index_offset int +func (a *PagedArray[T]) Each() func(yield func(int, *T) bool) { + return func(yield func(int, *T) bool) { + var page *ArrayPage[T] + var index_offset int - book := a.book + book := a.book - if a.len == 0 { - return - } + if a.len == 0 { + return + } - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - index_offset = i << pageSizeShift + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] + index_offset = i << pageSizeShift - for j := page.len - 1; j >= 0; j-- { - if !yield(index_offset+j, &page.data[j]) { - return + for j := page.len - 1; j >= 0; j-- { + if !yield(index_offset+j, &page.data[j]) { + return + } } } } } -func (a *PagedArray[T]) EachParallel(numWorkers int, yield func(int, *T, int) bool) { - assert.True(numWorkers > 0) - var chunkSize = a.len / numWorkers - var wg sync.WaitGroup - - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = a.len - } - go func(start int, end int) { - defer wg.Done() - r := end - start - for i := range r { - if !yield(i, a.Get(i+startIndex), workedId) { - return - } +func (a *PagedArray[T]) EachParallel(numWorkers int) func(yield func(int, *T, int) bool) { + return func(yield func(int, *T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers + var wg sync.WaitGroup + + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } - }(startIndex, endIndex) + go func(start int, end int) { + defer wg.Done() + r := end - start + for i := range r { + if !yield(i, a.Get(i+startIndex), workedId) { + return + } + } + }(startIndex, endIndex) + } + wg.Wait() } - wg.Wait() } -func (a *PagedArray[T]) EachData(yield func(*T) bool) { - var page *ArrayPage[T] - - book := a.book +func (a *PagedArray[T]) EachData() func(yield func(*T) bool) { + return func(yield func(*T) bool) { + var page *ArrayPage[T] + var book = a.book - if a.len == 0 { - return - } + if a.len == 0 { + return + } - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] - for j := page.len - 1; j >= 0; j-- { - if !yield(&page.data[j]) { - return + for j := page.len - 1; j >= 0; j-- { + if !yield(&page.data[j]) { + return + } } } } } -func (a *PagedArray[T]) EachDataValue(yield func(T) bool) { - var page *ArrayPage[T] - - book := a.book +func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { + return func(yield func(T) bool) { + var page *ArrayPage[T] + var book = a.book - if a.len == 0 { - return - } + if a.len == 0 { + return + } - for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] - for j := page.len - 1; j >= 0; j-- { - if !yield(page.data[j]) { - return + for j := page.len - 1; j >= 0; j-- { + if !yield(page.data[j]) { + return + } } } } } -func (a *PagedArray[T]) EachDataValueParallel(numWorkers int, yield func(T, int) bool) { - assert.True(numWorkers > 0) - var chunkSize = a.len / numWorkers - var wg sync.WaitGroup - - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = a.len - } - go func(start int, end int) { - defer wg.Done() - r := end - start - for i := range r { - if !yield(a.GetValue(i+startIndex), workedId) { - return - } +func (a *PagedArray[T]) EachDataValueParallel(numWorkers int) func(yield func(T, int) bool) { + return func(yield func(T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers + var wg sync.WaitGroup + + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } - }(startIndex, endIndex) + go func(start int, end int) { + defer wg.Done() + r := end - start + for i := range r { + if !yield(a.GetValue(i+startIndex), workedId) { + return + } + } + }(startIndex, endIndex) + } + wg.Wait() } - wg.Wait() } -func (a *PagedArray[T]) EachDataParallel(numWorkers int, yield func(*T, int) bool) { - assert.True(numWorkers > 0) - var chunkSize = a.len / numWorkers - var wg sync.WaitGroup - - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = a.len - } - go func(start int, end int) { - defer wg.Done() - r := end - start - for i := range r { - if !yield(a.Get(i+startIndex), workedId) { - return - } +func (a *PagedArray[T]) EachDataParallel(numWorkers int) func(yield func(*T, int) bool) { + return func(yield func(*T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = a.len / numWorkers + var wg sync.WaitGroup + + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = a.len } - }(startIndex, endIndex) + go func(start int, end int) { + defer wg.Done() + r := end - start + for i := range r { + if !yield(a.Get(i+startIndex), workedId) { + return + } + } + }(startIndex, endIndex) + } + wg.Wait() } - wg.Wait() } type SlicePage[T any] struct { diff --git a/pkg/ecs/slice.go b/pkg/ecs/slice.go index 5567bfb7..4a3c5785 100644 --- a/pkg/ecs/slice.go +++ b/pkg/ecs/slice.go @@ -2,6 +2,14 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +<- Саратов Рай Donated 1 000 RUB + +Thank you for your support! */ package ecs @@ -12,9 +20,10 @@ import ( "github.com/negrel/assert" ) +// Slice tricks https://ueokande.github.io/go-slice-tricks/ + type Slice[T any] struct { data []T - len int } func NewSlice[T any](size int) (a Slice[T]) { @@ -23,54 +32,57 @@ func NewSlice[T any](size int) (a Slice[T]) { } func (a *Slice[T]) Len() int { - return a.len + return len(a.data) } func (a *Slice[T]) Get(index int) *T { assert.True(index >= 0, "index out of range") - assert.True(index < a.len, "index out of range") + assert.True(index < len(a.data), "index out of range") return &a.data[index] } func (a *Slice[T]) GetValue(index int) T { assert.True(index >= 0, "index out of range") - assert.True(index < a.len, "index out of range") + assert.True(index < len(a.data), "index out of range") return a.data[index] } func (a *Slice[T]) Set(index int, value T) *T { assert.True(index >= 0, "index out of range") - assert.True(index < a.len, "index out of range") + assert.True(index < len(a.data), "index out of range") a.data[index] = value return &a.data[index] } -func (a *Slice[T]) Append(values ...T) *T { - a.data = append(a.data[:a.len], values...) - a.len += len(values) - return &a.data[a.len-1] +func (a *Slice[T]) Append(values ...T) []T { + a.data = append(a.data, values...) + disCap := 1 << (FastIntLog2(len(a.data)) + 1) + if cap(a.data) > disCap { + a.data = a.data[:len(a.data):disCap] + } + return a.data } func (a *Slice[T]) SoftReduce() { - assert.True(a.len > 0, "Len is already 0") - a.len-- + assert.True(len(a.data) > 0, "Len is already 0") + a.data = a.data[:len(a.data)-1] } func (a *Slice[T]) Reset() { - a.len = 0 + a.data = a.data[:0] } func (a *Slice[T]) Copy(fromIndex, toIndex int) { assert.True(fromIndex >= 0, "index out of range") - assert.True(fromIndex < a.len, "index out of range") + assert.True(fromIndex < len(a.data), "index out of range") from := a.Get(fromIndex) assert.True(toIndex >= 0, "index out of range") - assert.True(toIndex < a.len, "index out of range") + assert.True(toIndex < len(a.data), "index out of range") to := a.Get(toIndex) *to = *from @@ -78,11 +90,11 @@ func (a *Slice[T]) Copy(fromIndex, toIndex int) { func (a *Slice[T]) Swap(i, j int) (newI, NewJ *T) { assert.True(i >= 0, "index out of range") - assert.True(i < a.len, "index out of range") + assert.True(i < len(a.data), "index out of range") x := a.Get(i) assert.True(j >= 0, "index out of range") - assert.True(j < a.len, "index out of range") + assert.True(j < len(a.data), "index out of range") y := a.Get(j) *x, *y = *y, *x @@ -90,7 +102,7 @@ func (a *Slice[T]) Swap(i, j int) (newI, NewJ *T) { } func (a *Slice[T]) Last() *T { - index := a.len - 1 + index := len(a.data) - 1 assert.True(index >= 0, "index out of range") return a.Get(index) @@ -110,98 +122,110 @@ func (a *Slice[T]) getPageIdAndIndex(index int) (int, int) { return pageId, index } -func (a *Slice[T]) Each(yield func(int, *T) bool) { - for j := a.len - 1; j >= 0; j-- { - if !yield(j, &a.data[j]) { - return +func (a *Slice[T]) Each() func(yield func(int, *T) bool) { + return func(yield func(int, *T) bool) { + for j := len(a.data) - 1; j >= 0; j-- { + if !yield(j, &a.data[j]) { + return + } } } } -func (a *Slice[T]) EachData(yield func(*T) bool) { - for j := a.len - 1; j >= 0; j-- { - if !yield(&a.data[j]) { - return +func (a *Slice[T]) EachData() func(yield func(*T) bool) { + return func(yield func(*T) bool) { + for j := len(a.data) - 1; j >= 0; j-- { + if !yield(&a.data[j]) { + return + } } } } -func (a *Slice[T]) EachDataValue(yield func(T) bool) { - for j := a.len - 1; j >= 0; j-- { - if !yield(a.data[j]) { - return +func (a *Slice[T]) EachDataValue() func(yield func(T) bool) { + return func(yield func(T) bool) { + for j := len(a.data) - 1; j >= 0; j-- { + if !yield(a.data[j]) { + return + } } } } -func (a *Slice[T]) EachParallel(numWorkers int, yield func(int, *T, int) bool) { - assert.True(numWorkers > 0) - var chunkSize = a.len / numWorkers - var wg sync.WaitGroup +func (a *Slice[T]) EachParallel(numWorkers int) func(yield func(int, *T, int) bool) { + return func(yield func(int, *T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = len(a.data) / numWorkers + var wg sync.WaitGroup - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = a.len - } - go func(start int, end int) { - defer wg.Done() - for i := range a.data[start:end] { - if !yield(i, &a.data[i+startIndex], workedId) { - return - } + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = len(a.data) } - }(startIndex, endIndex) + go func(start int, end int) { + defer wg.Done() + for i := range a.data[start:end] { + if !yield(i, &a.data[i+startIndex], workedId) { + return + } + } + }(startIndex, endIndex) + } + wg.Wait() } - wg.Wait() } -func (a *Slice[T]) EachDataValueParallel(numWorkers int, yield func(T, int) bool) { - assert.True(numWorkers > 0) - var chunkSize = a.len / numWorkers - var wg sync.WaitGroup +func (a *Slice[T]) EachDataValueParallel(numWorkers int) func(yield func(T, int) bool) { + return func(yield func(T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = len(a.data) / numWorkers + var wg sync.WaitGroup - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = a.len - } - go func(start int, end int) { - defer wg.Done() - for i := range a.data[start:end] { - if !yield(a.data[i+startIndex], workedId) { - return - } + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = len(a.data) } - }(startIndex, endIndex) + go func(start int, end int) { + defer wg.Done() + for i := range a.data[start:end] { + if !yield(a.data[i+startIndex], workedId) { + return + } + } + }(startIndex, endIndex) + } + wg.Wait() } - wg.Wait() } -func (a *Slice[T]) EachDataParallel(numWorkers int, yield func(*T, int) bool) { - assert.True(numWorkers > 0) - var chunkSize = a.len / numWorkers - var wg sync.WaitGroup +func (a *Slice[T]) EachDataParallel(numWorkers int) func(yield func(*T, int) bool) { + return func(yield func(*T, int) bool) { + assert.True(numWorkers > 0) + var chunkSize = len(a.data) / numWorkers + var wg sync.WaitGroup - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = a.len - } - go func(start int, end int) { - defer wg.Done() - for i := range a.data[start:end] { - if !yield(&a.data[i+startIndex], workedId) { - return - } + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = len(a.data) } - }(startIndex, endIndex) + go func(start int, end int) { + defer wg.Done() + for i := range a.data[start:end] { + if !yield(&a.data[i+startIndex], workedId) { + return + } + } + }(startIndex, endIndex) + } + wg.Wait() } - wg.Wait() } diff --git a/stdsystems/animation-player.go b/stdsystems/animation-player.go index ec397316..7e04282e 100644 --- a/stdsystems/animation-player.go +++ b/stdsystems/animation-player.go @@ -28,42 +28,43 @@ func (s *AnimationPlayerSystem) Init() { } func (s *AnimationPlayerSystem) Run() { dt := time.Since(s.lastRunAt) - s.AnimationPlayers.EachComponent(func(animation *stdcomponents.AnimationPlayer) bool { - animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond + s.AnimationPlayers.EachComponent()( + func(animation *stdcomponents.AnimationPlayer) bool { + animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond - assert.True(animation.FrameDuration > 0, "frame duration must be greater than 0") + assert.True(animation.FrameDuration > 0, "frame duration must be greater than 0") - // Check if animation is playing backwards - if animation.Speed < 0 { - for animation.ElapsedTime <= 0 { - animation.ElapsedTime += animation.FrameDuration - animation.Current-- + // Check if animation is playing backwards + if animation.Speed < 0 { + for animation.ElapsedTime <= 0 { + animation.ElapsedTime += animation.FrameDuration + animation.Current-- - if animation.Current < animation.First { - if animation.Loop { - animation.Current = animation.Last - } else { - animation.Current = animation.First + if animation.Current < animation.First { + if animation.Loop { + animation.Current = animation.Last + } else { + animation.Current = animation.First + } } } - } - } else { - for animation.ElapsedTime >= animation.FrameDuration { - animation.ElapsedTime -= animation.FrameDuration - animation.Current++ + } else { + for animation.ElapsedTime >= animation.FrameDuration { + animation.ElapsedTime -= animation.FrameDuration + animation.Current++ - if animation.Current > animation.Last { - if animation.Loop { - animation.Current = animation.First - } else { - animation.Current = animation.Last + if animation.Current > animation.Last { + if animation.Loop { + animation.Current = animation.First + } else { + animation.Current = animation.Last + } } } } - } - return true - }) + return true + }) s.lastRunAt = time.Now() } func (s *AnimationPlayerSystem) Destroy() {} diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index 62bd10c6..634056bc 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -25,7 +25,7 @@ type AnimationSpriteMatrixSystem struct { func (s *AnimationSpriteMatrixSystem) Init() {} func (s *AnimationSpriteMatrixSystem) Run() { - s.AnimationPlayers.EachEntity(func(e ecs.Entity) bool { + s.AnimationPlayers.EachEntity()(func(e ecs.Entity) bool { animationPlayer := s.AnimationPlayers.GetUnsafe(e) spriteMatrix := s.SpriteMatrixes.Get(e) if spriteMatrix == nil { diff --git a/stdsystems/collider.go b/stdsystems/collider.go index fbcfbe4f..b8458257 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -40,7 +40,7 @@ type ColliderSystem struct { func (s *ColliderSystem) Init() {} func (s *ColliderSystem) Run(dt time.Duration) { - s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { + s.BoxColliders.EachEntity()(func(entity ecs.Entity) bool { boxCollider := s.BoxColliders.GetUnsafe(entity) genCollider := s.GenericColliders.GetUnsafe(entity) @@ -81,7 +81,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.CircleColliders.EachEntity(func(entity ecs.Entity) bool { + s.CircleColliders.EachEntity()(func(entity ecs.Entity) bool { circleCollider := s.CircleColliders.GetUnsafe(entity) genCollider := s.GenericColliders.GetUnsafe(entity) @@ -111,7 +111,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.GenericColliders.EachEntity(func(entity ecs.Entity) bool { + s.GenericColliders.EachEntity()(func(entity ecs.Entity) bool { genCollider := s.GenericColliders.GetUnsafe(entity) if genCollider.AllowSleep { diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index d375f408..128bf8d6 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -78,7 +78,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } // Fill trees - s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + s.GenericCollider.EachEntity()(func(entity ecs.Entity) bool { aabb := s.AABB.GetUnsafe(entity) layer := s.GenericCollider.GetUnsafe(entity).Layer @@ -151,7 +151,7 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity func (s *CollisionDetectionBVHSystem) registerCollisionEvents() { for i := range s.collisionEvents { events := &s.collisionEvents[i] - events.EachData(func(event *CollisionEvent) bool { + events.EachData()(func(event *CollisionEvent) bool { pair := CollisionPair{event.entityA, event.entityB} s.currentCollisions[pair] = struct{}{} displacement := event.normal.Scale(event.depth) diff --git a/stdsystems/collision-detection-grid.go b/stdsystems/collision-detection-grid.go index bfa5cb21..496b963b 100644 --- a/stdsystems/collision-detection-grid.go +++ b/stdsystems/collision-detection-grid.go @@ -89,7 +89,7 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { s.currentCollisions = make(map[CollisionPair]struct{}) // Build spatial buckets and entity-to-cell map - s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + s.GenericCollider.EachEntity()(func(entity ecs.Entity) bool { position := s.Positions.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) @@ -104,7 +104,7 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { // Precompute AABBs for box colliders s.aabbs = make(map[ecs.Entity]aabb, s.BoxColliders.Len()) - s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { + s.BoxColliders.EachEntity()(func(entity ecs.Entity) bool { position := s.Positions.GetUnsafe(entity) collider := s.BoxColliders.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index c734806d..a98fcc1d 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -63,7 +63,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { func (s *CollisionDetectionSystem) Destroy() {} func (s *CollisionDetectionSystem) setup() { // Reset grids - s.CollisionGridComponentManager.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { + s.CollisionGridComponentManager.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { grid := s.CollisionGridComponentManager.GetUnsafe(entity) assert.NotNil(grid) grid.Entities.Reset() @@ -76,7 +76,7 @@ func (s *CollisionDetectionSystem) setup() { // Accumulate used CollisionLayers var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, s.numWorkers) - s.GenericCollider.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { + s.GenericCollider.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { collider := s.GenericCollider.GetUnsafe(entity) assert.NotNil(collider) collisionLayerAccumulators[workerId] |= 1 << collider.Layer @@ -110,7 +110,7 @@ func (s *CollisionDetectionSystem) setup() { } // Register all entities in grids - s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + s.GenericCollider.EachEntity()(func(entity ecs.Entity) bool { collider := s.GenericCollider.GetUnsafe(entity) assert.NotNil(collider) @@ -131,7 +131,7 @@ func (s *CollisionDetectionSystem) setup() { }) // Distribute entities in grids - s.CollisionGridComponentManager.EachEntity(func(gridEntity ecs.Entity) bool { + s.CollisionGridComponentManager.EachEntity()(func(gridEntity ecs.Entity) bool { grid := s.CollisionGridComponentManager.GetUnsafe(gridEntity) assert.NotNil(grid) @@ -147,7 +147,7 @@ func (s *CollisionDetectionSystem) setup() { grid.ChunkSize = newChunkSize } - grid.Entities.EachDataValue(func(entity ecs.Entity) bool { + grid.Entities.EachDataValue()(func(entity ecs.Entity) bool { position := s.Positions.GetUnsafe(entity) assert.NotNil(position) @@ -194,7 +194,7 @@ func (s *CollisionDetectionSystem) setup() { return true }) - s.CollisionChunkComponentManager.EachEntityParallel(s.numWorkers, func(chunkEntity ecs.Entity, workerId int) bool { + s.CollisionChunkComponentManager.EachEntityParallel(s.numWorkers)(func(chunkEntity ecs.Entity, workerId int) bool { tree := s.BvhTreeComponentManager.GetUnsafe(chunkEntity) assert.NotNil(tree) tree.Build() diff --git a/stdsystems/collision-reslution.go b/stdsystems/collision-reslution.go index dfae9367..57f53363 100644 --- a/stdsystems/collision-reslution.go +++ b/stdsystems/collision-reslution.go @@ -35,7 +35,7 @@ type CollisionResolutionSystem struct { func (s *CollisionResolutionSystem) Init() {} func (s *CollisionResolutionSystem) Run(dt time.Duration) { - s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { + s.Collisions.EachComponent()(func(collision *stdcomponents.Collision) bool { if collision.State == stdcomponents.CollisionStateEnter || collision.State == stdcomponents.CollisionStateStay { // Resolve penetration var displacement vectors.Vec2 diff --git a/stdsystems/culling.go b/stdsystems/culling.go index 26210ab1..67a684df 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -10,6 +10,7 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" + "runtime" "time" ) @@ -25,20 +26,22 @@ type CullingSystem struct { AABBs *stdcomponents.AABBComponentManager Cameras *stdcomponents.CameraComponentManager RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager + numWorkers int } func (s *CullingSystem) Init() { + s.numWorkers = runtime.NumCPU() - 2 } func (s *CullingSystem) Run(dt time.Duration) { - s.Renderables.EachComponentParallel(2048, func(r *stdcomponents.Renderable, i int) bool { + s.Renderables.EachComponentParallel(s.numWorkers)(func(r *stdcomponents.Renderable, i int) bool { r.Observed = false return true }) - s.Cameras.EachEntity(func(entity ecs.Entity) bool { + s.Cameras.EachEntity()(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) cameraRect := camera.Rect() - s.Renderables.EachEntity(func(entity ecs.Entity) bool { + s.Renderables.EachEntity()(func(entity ecs.Entity) bool { renderable := s.Renderables.GetUnsafe(entity) //renderVisible := s.RenderVisible.GetUnsafe(entity) aabb := s.AABBs.GetUnsafe(entity) @@ -62,7 +65,7 @@ func (s *CullingSystem) Run(dt time.Duration) { }) return true }) - s.Renderables.EachEntity(func(entity ecs.Entity) bool { + s.Renderables.EachEntity()(func(entity ecs.Entity) bool { renderable := s.Renderables.GetUnsafe(entity) visible := s.RenderVisible.GetUnsafe(entity) if visible == nil { diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index 4f9d02d1..ba67c8c5 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -70,7 +70,7 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { s.renderObjectsSorted = make([]renderObjectSorted, 0, s.RenderVisibles.Len()) } - s.RenderVisibles.EachEntity(func(entity ecs.Entity) bool { + s.RenderVisibles.EachEntity()(func(entity ecs.Entity) bool { o := s.RenderOrders.GetUnsafe(entity) assert.NotNil(o) @@ -107,7 +107,7 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { }) } - s.Cameras.EachEntity(func(cameraEntity ecs.Entity) bool { + s.Cameras.EachEntity()(func(cameraEntity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(cameraEntity) assert.NotNil(camera) renderTexture := s.RenderTexture2D.GetUnsafe(cameraEntity) @@ -153,7 +153,7 @@ func (s *Render2DCamerasSystem) prepareRender(dt time.Duration) { func (s *Render2DCamerasSystem) prepareAnimations(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) animation := s.AnimationPlayers.GetUnsafe(entity) if animation == nil { @@ -171,7 +171,7 @@ func (s *Render2DCamerasSystem) prepareAnimations(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) flipped := s.Flips.GetUnsafe(entity) if flipped == nil { @@ -190,7 +190,7 @@ func (s *Render2DCamerasSystem) prepareFlips(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() //dts := dt.Seconds() - s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) position := s.Positions.GetUnsafe(entity) if position == nil { @@ -208,7 +208,7 @@ func (s *Render2DCamerasSystem) preparePositions(wg *sync.WaitGroup, dt time.Dur func (s *Render2DCamerasSystem) prepareRotations(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) rotation := s.Rotations.GetUnsafe(entity) if rotation == nil { @@ -221,7 +221,7 @@ func (s *Render2DCamerasSystem) prepareRotations(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) prepareScales(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) if scale == nil { @@ -235,7 +235,7 @@ func (s *Render2DCamerasSystem) prepareScales(wg *sync.WaitGroup) { func (s *Render2DCamerasSystem) prepareTints(wg *sync.WaitGroup) { defer wg.Done() - s.Textures.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { tr := s.Textures.GetUnsafe(entity) tint := s.Tints.GetUnsafe(entity) if tint == nil { diff --git a/stdsystems/render.go b/stdsystems/render.go index fd3d53fc..9f21adac 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -47,7 +47,7 @@ func (s *RenderSystem) Init() { func (s *RenderSystem) Run(dt time.Duration) bool { - s.FrameBuffer2D.EachComponent(func(c *stdcomponents.FrameBuffer2D) bool { + s.FrameBuffer2D.EachComponent()(func(c *stdcomponents.FrameBuffer2D) bool { s.frames = append(s.frames, *c) return true }) diff --git a/stdsystems/sprite-matrix.go b/stdsystems/sprite-matrix.go index 537b19fa..83fcdf53 100644 --- a/stdsystems/sprite-matrix.go +++ b/stdsystems/sprite-matrix.go @@ -26,7 +26,7 @@ type SpriteMatrixSystem struct { func (s *SpriteMatrixSystem) Init() {} func (s *SpriteMatrixSystem) Run() { - s.SpriteMatrixes.EachEntity(func(entity ecs.Entity) bool { + s.SpriteMatrixes.EachEntity()(func(entity ecs.Entity) bool { spriteMatrix := s.SpriteMatrixes.Get(entity) // position := s.Positions.GetUnsafe(entity) animationState := s.AnimationStates.GetUnsafe(entity) // diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index c40ad225..602a9419 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -35,7 +35,7 @@ type SpriteSystem struct { func (s *SpriteSystem) Init() {} func (s *SpriteSystem) Run() { - s.Sprites.EachEntity(func(entity ecs.Entity) bool { + s.Sprites.EachEntity()(func(entity ecs.Entity) bool { sprite := s.Sprites.GetUnsafe(entity) // position := s.Positions.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) diff --git a/stdsystems/texture-position-smooth.go b/stdsystems/texture-position-smooth.go index bf1bae49..5f40e114 100644 --- a/stdsystems/texture-position-smooth.go +++ b/stdsystems/texture-position-smooth.go @@ -35,26 +35,26 @@ func (s *TexturePositionSmoothSystem) Init() { func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { //DEBUG Temporary, TODO: remove if rl.IsKeyPressed(rl.KeyI) { - s.TexturePositionSmooth.EachComponentParallel(s.numWorkers, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.numWorkers)(func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothOff return true }) } if rl.IsKeyPressed(rl.KeyO) { - s.TexturePositionSmooth.EachComponentParallel(s.numWorkers, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.numWorkers)(func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothLerp return true }) } if rl.IsKeyPressed(rl.KeyP) { - s.TexturePositionSmooth.EachComponentParallel(s.numWorkers, func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.numWorkers)(func(t *stdcomponents.TexturePositionSmooth, i int) bool { *t = stdcomponents.TexturePositionSmoothExpDecay return true }) } //END DEBUG - s.TexturePositionSmooth.EachEntityParallel(s.numWorkers, func(entity ecs.Entity, i int) bool { + s.TexturePositionSmooth.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, i int) bool { position := s.Position.GetUnsafe(entity) texture := s.RLTexture.GetUnsafe(entity) smooth := s.TexturePositionSmooth.GetUnsafe(entity) diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index e7c00d40..d614dc2c 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -29,7 +29,7 @@ func (s *VelocitySystem) Init() {} func (s *VelocitySystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - s.Velocities.EachEntity(func(e ecs.Entity) bool { + s.Velocities.EachEntity()(func(e ecs.Entity) bool { velocity := s.Velocities.GetUnsafe(e) assert.True(s.isVelocityValid(velocity)) diff --git a/stdsystems/ysort.go b/stdsystems/ysort.go index 38b693d6..bc2c0360 100644 --- a/stdsystems/ysort.go +++ b/stdsystems/ysort.go @@ -34,7 +34,7 @@ type YSortSystem struct { func (s *YSortSystem) Init() {} func (s *YSortSystem) Run() { - s.YSorts.EachEntity(func(entity ecs.Entity) bool { + s.YSorts.EachEntity()(func(entity ecs.Entity) bool { pos := s.Positions.GetUnsafe(entity) renderOrder := s.RenderOrders.GetUnsafe(entity) diff --git a/taskfile.yml b/taskfile.yml index 0e042bfd..8870a661 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -5,7 +5,7 @@ tasks: env: CGO_ENABLED: 1 cmds: - - go run ./examples/new-api/game.go + - go run ./examples/new-api server: cmds: From 43e6fbbaa7c00b8c399bc2e6794c7c631444954b Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 14 Apr 2025 21:54:44 +0300 Subject: [PATCH 138/196] massive optimizations --- pkg/ecs/paged-array.go | 10 +-- pkg/ecs/paged-map.go | 2 +- stdcomponents/rl-texture-pro.go | 10 +++ stdsystems/bvh-tree.go | 3 +- stdsystems/collider.go | 108 +++++++++++++++++++++----- stdsystems/collision-detection-bvh.go | 46 +++-------- stdsystems/culling.go | 48 +++++++----- stdsystems/render.go | 1 - stdsystems/sprite.go | 72 ++++++++++++----- stdsystems/velocity.go | 9 ++- 10 files changed, 204 insertions(+), 105 deletions(-) diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 19857fd5..cecc8735 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -270,15 +270,15 @@ func (a *PagedArray[T]) EachDataValueParallel(numWorkers int) func(yield func(T, if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker endIndex = a.len } - go func(start int, end int) { - defer wg.Done() - r := end - start + go func(y func(T, int) bool, s int, e int, id int, w *sync.WaitGroup) { + defer w.Done() + r := e - s for i := range r { - if !yield(a.GetValue(i+startIndex), workedId) { + if !y(a.GetValue(i+s), id) { return } } - }(startIndex, endIndex) + }(yield, startIndex, endIndex, workedId, &wg) } wg.Wait() } diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 10247a18..6666c1a5 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -7,7 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs const ( - pageSizeShift = 12 + pageSizeShift = 10 pageSize = 1 << pageSizeShift initialBookSize = 4 // Starting with a small initial book size ) diff --git a/stdcomponents/rl-texture-pro.go b/stdcomponents/rl-texture-pro.go index 26f4dc39..21effe6e 100644 --- a/stdcomponents/rl-texture-pro.go +++ b/stdcomponents/rl-texture-pro.go @@ -17,6 +17,7 @@ package stdcomponents import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" + "gomp/vectors" "image/color" ) @@ -29,6 +30,15 @@ type RLTexturePro struct { Rotation float32 } +func (t *RLTexturePro) Rect() vectors.Rectangle { + return vectors.Rectangle{ + X: t.Dest.X + t.Origin.X, + Y: t.Dest.Y + t.Origin.Y, + Width: t.Dest.Width, + Height: t.Dest.Height, + } +} + type RLTextureProComponentManager = ecs.ComponentManager[RLTexturePro] func NewRlTextureProComponentManager() RLTextureProComponentManager { diff --git a/stdsystems/bvh-tree.go b/stdsystems/bvh-tree.go index f4e62f36..1b1d2764 100644 --- a/stdsystems/bvh-tree.go +++ b/stdsystems/bvh-tree.go @@ -15,6 +15,7 @@ Thank you for your support! package stdsystems import ( + "cmp" "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" @@ -46,7 +47,7 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { var sorted = t.Components.Raw() slices.SortFunc(sorted, func(a, b stdcomponents.BvhComponent) int { - return int(a.Code - b.Code) + return cmp.Compare(a.Code, b.Code) }) // Add leaves diff --git a/stdsystems/collider.go b/stdsystems/collider.go index b8458257..822db91b 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -15,9 +15,11 @@ Thank you for your support! package stdsystems import ( + "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" + "runtime" "time" ) @@ -36,17 +38,52 @@ type ColliderSystem struct { CircleColliders *stdcomponents.CircleColliderComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager AABB *stdcomponents.AABBComponentManager + + numWorkers int } -func (s *ColliderSystem) Init() {} +func (s *ColliderSystem) Init() { + s.numWorkers = runtime.NumCPU() - 2 +} func (s *ColliderSystem) Run(dt time.Duration) { - s.BoxColliders.EachEntity()(func(entity ecs.Entity) bool { + var accAABB = make([][]ecs.Entity, s.numWorkers) + var accGenericColliders = make([][]ecs.Entity, s.numWorkers) + s.BoxColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + if !s.GenericColliders.Has(entity) { + accGenericColliders[workerId] = append(accGenericColliders[workerId], entity) + } + if !s.AABB.Has(entity) { + accAABB[workerId] = append(accAABB[workerId], entity) + } + return true + }) + s.CircleColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + if !s.GenericColliders.Has(entity) { + accGenericColliders[workerId] = append(accGenericColliders[workerId], entity) + } + if !s.AABB.Has(entity) { + accAABB[workerId] = append(accAABB[workerId], entity) + } + return true + }) + for i := range accAABB { + a := accAABB[i] + for _, entity := range a { + s.AABB.Create(entity, stdcomponents.AABB{}) + } + } + for i := range accGenericColliders { + a := accGenericColliders[i] + for _, entity := range a { + s.GenericColliders.Create(entity, stdcomponents.GenericCollider{}) + } + } + + s.BoxColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { boxCollider := s.BoxColliders.GetUnsafe(entity) genCollider := s.GenericColliders.GetUnsafe(entity) - if genCollider == nil { - genCollider = s.GenericColliders.Create(entity, stdcomponents.GenericCollider{}) - } + genCollider.Layer = boxCollider.Layer genCollider.Mask = boxCollider.Mask genCollider.Offset.X = boxCollider.Offset.X @@ -54,13 +91,24 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider.Shape = stdcomponents.BoxColliderShape genCollider.AllowSleep = boxCollider.AllowSleep + return true + }) + + s.BoxColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + boxCollider := s.BoxColliders.GetUnsafe(entity) + assert.NotNil(boxCollider) + position := s.Positions.GetUnsafe(entity) + assert.NotNil(position) + scale := s.Scales.GetUnsafe(entity) + assert.NotNil(scale) + rotation := s.Rotations.GetUnsafe(entity) + assert.NotNil(rotation) + aabb := s.AABB.GetUnsafe(entity) - if aabb == nil { - aabb = s.AABB.Create(entity, stdcomponents.AABB{}) - } + assert.NotNil(aabb) a := boxCollider.WH b := vectors.Vec2{X: 0, Y: boxCollider.WH.Y} @@ -81,13 +129,12 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.CircleColliders.EachEntity()(func(entity ecs.Entity) bool { + s.CircleColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { circleCollider := s.CircleColliders.GetUnsafe(entity) + assert.NotNil(circleCollider) genCollider := s.GenericColliders.GetUnsafe(entity) - if genCollider == nil { - genCollider = s.GenericColliders.Create(entity, stdcomponents.GenericCollider{}) - } + assert.NotNil(genCollider) genCollider.Layer = circleCollider.Layer genCollider.Mask = circleCollider.Mask @@ -96,12 +143,21 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider.Shape = stdcomponents.CircleColliderShape genCollider.AllowSleep = circleCollider.AllowSleep + return true + }) + + s.CircleColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + circleCollider := s.CircleColliders.GetUnsafe(entity) + assert.NotNil(circleCollider) + position := s.Positions.GetUnsafe(entity) + assert.NotNil(position) + scale := s.Scales.GetUnsafe(entity) + assert.NotNil(scale) + aabb := s.AABB.GetUnsafe(entity) - if aabb == nil { - aabb = s.AABB.Create(entity, stdcomponents.AABB{}) - } + assert.NotNil(aabb) offset := circleCollider.Offset.Mul(scale.XY) scaledRadius := scale.XY.Scale(circleCollider.Radius) @@ -111,9 +167,10 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.GenericColliders.EachEntity()(func(entity ecs.Entity) bool { + var accColliderSleepCreate = make([][]ecs.Entity, s.numWorkers) + var accColliderSleepDelete = make([][]ecs.Entity, s.numWorkers) + s.GenericColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { genCollider := s.GenericColliders.GetUnsafe(entity) - if genCollider.AllowSleep { shouldSleep := true velocity := s.Velocities.GetUnsafe(entity) @@ -125,15 +182,28 @@ func (s *ColliderSystem) Run(dt time.Duration) { isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(entity) if shouldSleep { if isSleeping == nil { - isSleeping = s.ColliderSleepStateComponentManager.Create(entity, stdcomponents.ColliderSleepState{}) + accColliderSleepCreate[workerId] = append(accColliderSleepCreate[workerId], entity) } } else { if isSleeping != nil { - s.ColliderSleepStateComponentManager.Delete(entity) + accColliderSleepDelete[workerId] = append(accColliderSleepDelete[workerId], entity) } } } return true }) + for i := range accColliderSleepCreate { + a := accColliderSleepCreate[i] + for _, entity := range a { + s.ColliderSleepStateComponentManager.Create(entity, stdcomponents.ColliderSleepState{}) + } + } + for i := range accColliderSleepDelete { + a := accColliderSleepDelete[i] + for _, entity := range a { + s.ColliderSleepStateComponentManager.Delete(entity) + } + } + } func (s *ColliderSystem) Destroy() {} diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 128bf8d6..dc7c7c9c 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -60,13 +60,14 @@ type CollisionDetectionBVHSystem struct { activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities currentCollisions map[CollisionPair]struct{} - entities []ecs.Entity + numWorkers int } func (s *CollisionDetectionBVHSystem) Init() { for i := range maxNumWorkers { s.collisionEvents[i] = ecs.NewPagedArray[CollisionEvent]() } + s.numWorkers = runtime.NumCPU() - 2 } func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { @@ -105,47 +106,22 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } wg.Wait() - if len(s.entities) < s.GenericCollider.Len() { - s.entities = make([]ecs.Entity, 0, s.GenericCollider.Len()) - } - s.entities = s.GenericCollider.RawEntities(s.entities) - s.findEntityCollisions(s.entities) + s.findEntityCollisions() s.registerCollisionEvents() } func (s *CollisionDetectionBVHSystem) Destroy() {} -func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity) { - var wg sync.WaitGroup - entitiesLength := len(entities) - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(entitiesLength/128, maxNumWorkers), 1) - chunkSize := entitiesLength / numWorkers - - wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = entitiesLength +func (s *CollisionDetectionBVHSystem) findEntityCollisions() { + s.GenericCollider.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + potentialEntities := s.broadPhase(entity, make([]ecs.Entity, 0, 64)) + if len(potentialEntities) == 0 { + return true } - go func(start int, end int, id int) { - defer wg.Done() - - for i := range entities[start:end] { - entityA := entities[i+startIndex] - - potentialEntities := s.broadPhase(entityA, make([]ecs.Entity, 0, 64)) - if len(potentialEntities) == 0 { - continue - } - s.narrowPhase(entityA, potentialEntities, id) - } - }(startIndex, endIndex, workedId) - } - // Wait for workers and close collision channel - wg.Wait() + s.narrowPhase(entity, potentialEntities, workerId) + return true + }) } func (s *CollisionDetectionBVHSystem) registerCollisionEvents() { diff --git a/stdsystems/culling.go b/stdsystems/culling.go index 67a684df..7006979c 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -7,6 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdsystems import ( + "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" @@ -38,47 +39,54 @@ func (s *CullingSystem) Run(dt time.Duration) { r.Observed = false return true }) + s.Cameras.EachEntity()(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) cameraRect := camera.Rect() - s.Renderables.EachEntity()(func(entity ecs.Entity) bool { + + s.Renderables.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { renderable := s.Renderables.GetUnsafe(entity) - //renderVisible := s.RenderVisible.GetUnsafe(entity) - aabb := s.AABBs.GetUnsafe(entity) + assert.NotNil(renderable) - switch camera.Culling { - case stdcomponents.Culling2DFullscreenBB: - //TODO: textureAABB - if aabb == nil { - renderable.Observed = true - return true - } - if s.intersects(cameraRect, aabb.Rect()) { - renderable.Observed = true - } + texture := s.Textures.GetUnsafe(entity) + assert.NotNil(texture) - default: + textureRect := texture.Rect() + + if s.intersects(cameraRect, textureRect) { renderable.Observed = true } - return true }) return true }) - s.Renderables.EachEntity()(func(entity ecs.Entity) bool { + + var accRenderVisibleCreate = make([][]ecs.Entity, s.numWorkers) + var accRenderVisibleDelete = make([][]ecs.Entity, s.numWorkers) + s.Renderables.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { renderable := s.Renderables.GetUnsafe(entity) - visible := s.RenderVisible.GetUnsafe(entity) - if visible == nil { + assert.NotNil(renderable) + if !s.RenderVisible.Has(entity) { if renderable.Observed { - s.RenderVisible.Create(entity, stdcomponents.RenderVisible{}) + accRenderVisibleCreate[workerId] = append(accRenderVisibleCreate[workerId], entity) } } else { if !renderable.Observed { - s.RenderVisible.Delete(entity) + accRenderVisibleDelete[workerId] = append(accRenderVisibleDelete[workerId], entity) } } return true }) + for a := range accRenderVisibleCreate { + for _, entity := range accRenderVisibleCreate[a] { + s.RenderVisible.Create(entity, stdcomponents.RenderVisible{}) + } + } + for a := range accRenderVisibleDelete { + for _, entity := range accRenderVisibleDelete[a] { + s.RenderVisible.Delete(entity) + } + } } func (_ *CullingSystem) intersects(rect1, rect2 vectors.Rectangle) bool { diff --git a/stdsystems/render.go b/stdsystems/render.go index 9f21adac..0a84f380 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -46,7 +46,6 @@ func (s *RenderSystem) Init() { } func (s *RenderSystem) Run(dt time.Duration) bool { - s.FrameBuffer2D.EachComponent()(func(c *stdcomponents.FrameBuffer2D) bool { s.frames = append(s.frames, *c) return true diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index 602a9419..1d2c3048 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -16,8 +16,10 @@ package stdsystems import ( rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" + "runtime" ) func NewSpriteSystem() SpriteSystem { @@ -31,37 +33,65 @@ type SpriteSystem struct { Sprites *stdcomponents.SpriteComponentManager RLTexturePros *stdcomponents.RLTextureProComponentManager RenderOrder *stdcomponents.RenderOrderComponentManager + + numWorkers int } -func (s *SpriteSystem) Init() {} +func (s *SpriteSystem) Init() { + s.numWorkers = runtime.NumCPU() - 2 +} func (s *SpriteSystem) Run() { - s.Sprites.EachEntity()(func(entity ecs.Entity) bool { - sprite := s.Sprites.GetUnsafe(entity) // + var accRenderOrder = make([][]ecs.Entity, s.numWorkers) + var accRLTexturePros = make([][]ecs.Entity, s.numWorkers) + s.Sprites.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + renderOrder := s.RenderOrder.GetUnsafe(entity) + if renderOrder == nil { + accRenderOrder[workerId] = append(accRenderOrder[workerId], entity) + } + tr := s.RLTexturePros.GetUnsafe(entity) + if tr == nil { + accRLTexturePros[workerId] = append(accRLTexturePros[workerId], entity) + } + return true + }) + for a := range accRenderOrder { + for _, entity := range accRenderOrder[a] { + s.RenderOrder.Create(entity, stdcomponents.RenderOrder{}) + } + } + for a := range accRLTexturePros { + for _, entity := range accRLTexturePros[a] { + s.RLTexturePros.Create(entity, stdcomponents.RLTexturePro{}) + } + } + + s.Sprites.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + sprite := s.Sprites.GetUnsafe(entity) + assert.NotNil(sprite) + position := s.Positions.GetUnsafe(entity) + assert.NotNil(position) + scale := s.Scales.GetUnsafe(entity) + assert.NotNil(scale) renderOrder := s.RenderOrder.GetUnsafe(entity) - if renderOrder == nil { - renderOrder = s.RenderOrder.Create(entity, stdcomponents.RenderOrder{}) - } + assert.NotNil(renderOrder) tr := s.RLTexturePros.GetUnsafe(entity) - if tr == nil { - s.RLTexturePros.Create(entity, stdcomponents.RLTexturePro{ - Texture: sprite.Texture, // - Frame: sprite.Frame, // - Origin: rl.Vector2{ - X: sprite.Origin.X * scale.XY.X, - Y: sprite.Origin.Y * scale.XY.Y, - }, - Dest: rl.Rectangle{X: position.XY.X, Y: position.XY.Y, Width: sprite.Frame.Width, Height: sprite.Frame.Height}, // - Tint: sprite.Tint, - }) - } else { - tr.Dest.Width = sprite.Frame.Width - tr.Dest.Height = sprite.Frame.Height - tr.Tint = sprite.Tint + assert.NotNil(tr) + + tr.Texture = sprite.Texture + tr.Frame = sprite.Frame + tr.Origin = rl.Vector2{ + X: sprite.Origin.X * scale.XY.X, + Y: sprite.Origin.Y * scale.XY.Y, } + tr.Dest.X = position.XY.X + tr.Dest.Y = position.XY.Y + tr.Dest.Width = sprite.Frame.Width + tr.Dest.Height = sprite.Frame.Height + tr.Tint = sprite.Tint return true }) } diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index d614dc2c..2b57805c 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -11,6 +11,7 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "math" + "runtime" "time" ) @@ -22,14 +23,18 @@ type VelocitySystem struct { Velocities *stdcomponents.VelocityComponentManager Positions *stdcomponents.PositionComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager + + numWorkers int } -func (s *VelocitySystem) Init() {} +func (s *VelocitySystem) Init() { + s.numWorkers = runtime.NumCPU() - 2 +} func (s *VelocitySystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - s.Velocities.EachEntity()(func(e ecs.Entity) bool { + s.Velocities.EachEntityParallel(s.numWorkers)(func(e ecs.Entity, _ int) bool { velocity := s.Velocities.GetUnsafe(e) assert.True(s.isVelocityValid(velocity)) From 29dd608d773b557306b9d16baeb1d79e09abc965 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 15 Apr 2025 01:56:43 +0300 Subject: [PATCH 139/196] small memaloc fixes --- pkg/bvh/tree.go | 29 +++++++------ pkg/collision/epa.go | 13 +++--- pkg/collision/gjk.go | 33 ++++++++------ pkg/ecs/paged-array.go | 59 ++++++++++++-------------- pkg/ecs/paged-map.go | 10 ++--- stdcomponents/colliders.go | 6 +-- stdsystems/collision-detection-bvh.go | 14 +++--- stdsystems/collision-detection-grid.go | 2 +- stdsystems/render-2d-cameras.go | 38 ++++++----------- taskfile.yml | 2 +- 10 files changed, 99 insertions(+), 107 deletions(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 0189d446..17ac0be7 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -15,6 +15,7 @@ Thank you for your support! package bvh import ( + "cmp" "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" @@ -81,13 +82,13 @@ func (t *Tree) Build() { // Extract and sort components by morton code if cap(t.componentsSlice) < t.components.Len() { - t.componentsSlice = make([]component, 0, t.components.Len()) + t.componentsSlice = make([]component, 0, max(cap(t.componentsSlice)*2, t.components.Len())) } t.componentsSlice = t.components.Raw(t.componentsSlice) slices.SortFunc(t.componentsSlice, func(a, b component) int { - return int(a.code - b.code) + return cmp.Compare(a.code, b.code) }) // Add leaves @@ -114,14 +115,15 @@ func (t *Tree) Build() { childrenCreated bool } - stack := []buildTask{ + stack := [64]buildTask{ {parentIndex: 0, start: 0, end: t.leaves.Len() - 1, childrenCreated: false}, } + stackLen := 1 - for len(stack) > 0 { + for stackLen > 0 { + stackLen-- // Pop the last task - task := stack[len(stack)-1] - stack = stack[:len(stack)-1] + task := stack[stackLen] if !task.childrenCreated { if task.start == task.end { @@ -142,28 +144,31 @@ func (t *Tree) Build() { t.nodes.Get(task.parentIndex).childIndex = int32(leftIndex) // Push parent task back with childrenCreated=true - stack = append(stack, buildTask{ + stack[stackLen] = buildTask{ parentIndex: task.parentIndex, start: task.start, end: task.end, childrenCreated: true, - }) + } + stackLen++ // Push right child task (split+1 to end) - stack = append(stack, buildTask{ + stack[stackLen] = buildTask{ parentIndex: leftIndex + 1, start: split + 1, end: task.end, childrenCreated: false, - }) + } + stackLen++ // Push left child task (start to split) - stack = append(stack, buildTask{ + stack[stackLen] = buildTask{ parentIndex: leftIndex, start: task.start, end: split, childrenCreated: false, - }) + } + stackLen++ } else { // Merge children's AABBs into parent leftChildIndex := int(t.nodes.Get(task.parentIndex).childIndex) diff --git a/pkg/collision/epa.go b/pkg/collision/epa.go index 0cc45a66..7c964f80 100644 --- a/pkg/collision/epa.go +++ b/pkg/collision/epa.go @@ -28,20 +28,19 @@ const ( EPA - Expanding Polytope Algorithm Based on https://dyn4j.org/2010/05/epa-expanding-polytope-algorithm/#epa-alternatives */ -func EPA( +func (s *GJK) EPA( a, b AnyCollider, - transformA, transformB *stdcomponents.Transform2d, - simplex *Simplex2d, + transformA, transformB stdcomponents.Transform2d, ) (vectors.Vec2, float32) { - polytope := simplex.toPolytope(make([]vectors.Vec2, 0, 6)) + polytope := s.simplex.toPolytope(make([]vectors.Vec2, 0, 6)) bestNormal := vectors.Vec2{} bestDistance := float32(math.MaxFloat32) bestTolerance := float32(math.MaxFloat32) for range maxIterations { - edge := findClosestEdge(polytope) - point := minkowskiSupport2d(a, b, transformA, transformB, edge.normal) + edge := s.findClosestEdge(polytope) + point := s.minkowskiSupport2d(a, b, transformA, transformB, edge.normal) distance := point.Dot(edge.normal) tolerance := distance - edge.distance if tolerance < epaMaxTolerance { @@ -59,7 +58,7 @@ func EPA( return bestNormal, bestDistance } -func findClosestEdge(polytope []vectors.Vec2) closestEdge { +func (s *GJK) findClosestEdge(polytope []vectors.Vec2) closestEdge { closest := closestEdge{ distance: float32(math.MaxFloat32), normal: vectors.Vec2{}, diff --git a/pkg/collision/gjk.go b/pkg/collision/gjk.go index 42f9f2d4..dbed497f 100644 --- a/pkg/collision/gjk.go +++ b/pkg/collision/gjk.go @@ -21,8 +21,16 @@ const ( maxIterations = 64 ) +func New() GJK { + return GJK{} +} + +type GJK struct { + simplex Simplex2d +} + type AnyCollider interface { - GetSupport(direction vectors.Vec2, transform *stdcomponents.Transform2d) vectors.Vec2 + GetSupport(direction vectors.Vec2, transform stdcomponents.Transform2d) vectors.Vec2 } /* @@ -30,34 +38,33 @@ CheckCollision - GJK, Distance, Closest Points https://www.youtube.com/watch?v=Qupqu1xe7Io https://dyn4j.org/2010/04/gjk-distance-closest-points/#gjk-distance */ -func CheckCollision( +func (s *GJK) CheckCollision( a, b AnyCollider, - transformA, transformB *stdcomponents.Transform2d, -) (Simplex2d, bool) { + transformA, transformB stdcomponents.Transform2d, +) bool { direction := vectors.Vec2{X: 1, Y: 0} - simplex := Simplex2d{} - p := minkowskiSupport2d(a, b, transformA, transformB, direction) - simplex.add(p.ToVec3()) + p := s.minkowskiSupport2d(a, b, transformA, transformB, direction) + s.simplex.add(p.ToVec3()) direction = p.Neg() for range maxIterations { - p = minkowskiSupport2d(a, b, transformA, transformB, direction) + p = s.minkowskiSupport2d(a, b, transformA, transformB, direction) if p.Dot(direction) < 0 { - return simplex, false + return false } - simplex.add(p.ToVec3()) + s.simplex.add(p.ToVec3()) - if simplex.do(&direction) { - return simplex, true + if s.simplex.do(&direction) { + return true } } panic("GJK infinite loop") } -func minkowskiSupport2d(a, b AnyCollider, transformA, transformB *stdcomponents.Transform2d, direction vectors.Vec2) vectors.Vec2 { +func (s *GJK) minkowskiSupport2d(a, b AnyCollider, transformA, transformB stdcomponents.Transform2d, direction vectors.Vec2) vectors.Vec2 { return a.GetSupport(direction, transformA).Sub(b.GetSupport(direction.Neg(), transformB)) } diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index cecc8735..fa62cdec 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -16,6 +16,7 @@ type PagedArray[T any] struct { book []ArrayPage[T] currentPageIndex int len int + wg sync.WaitGroup } func NewPagedArray[T any]() (a PagedArray[T]) { @@ -155,13 +156,7 @@ func (a *PagedArray[T]) Raw(result []T) []T { } func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { - pageId := index >> pageSizeShift - assert.True(pageId < len(a.book), "index out of range") - - index %= pageSize - assert.True(index < pageSize, "index out of range") - - return pageId, index + return index >> pageSizeShift, index % pageSize } func (a *PagedArray[T]) Each() func(yield func(int, *T) bool) { @@ -261,26 +256,27 @@ func (a *PagedArray[T]) EachDataValueParallel(numWorkers int) func(yield func(T, return func(yield func(T, int) bool) { assert.True(numWorkers > 0) var chunkSize = a.len / numWorkers - var wg sync.WaitGroup - wg.Add(numWorkers) + a.wg.Add(numWorkers) for workedId := 0; workedId < numWorkers; workedId++ { startIndex := workedId * chunkSize endIndex := startIndex + chunkSize - 1 if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker endIndex = a.len } - go func(y func(T, int) bool, s int, e int, id int, w *sync.WaitGroup) { - defer w.Done() - r := e - s - for i := range r { - if !y(a.GetValue(i+s), id) { - return - } - } - }(yield, startIndex, endIndex, workedId, &wg) + go a.edvpTask(yield, startIndex, endIndex, workedId, &a.wg) + } + a.wg.Wait() + } +} + +func (a *PagedArray[T]) edvpTask(y func(T, int) bool, s int, e int, id int, w *sync.WaitGroup) { + defer w.Done() + r := e - s + for i := range r { + if !y(a.GetValue(i+s), id) { + return } - wg.Wait() } } @@ -288,26 +284,27 @@ func (a *PagedArray[T]) EachDataParallel(numWorkers int) func(yield func(*T, int return func(yield func(*T, int) bool) { assert.True(numWorkers > 0) var chunkSize = a.len / numWorkers - var wg sync.WaitGroup - wg.Add(numWorkers) + a.wg.Add(numWorkers) for workedId := 0; workedId < numWorkers; workedId++ { startIndex := workedId * chunkSize endIndex := startIndex + chunkSize - 1 if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker endIndex = a.len } - go func(start int, end int) { - defer wg.Done() - r := end - start - for i := range r { - if !yield(a.Get(i+startIndex), workedId) { - return - } - } - }(startIndex, endIndex) + go a.edpTask(yield, startIndex, endIndex, workedId, &a.wg) + } + a.wg.Wait() + } +} + +func (a *PagedArray[T]) edpTask(y func(*T, int) bool, s int, e int, id int, w *sync.WaitGroup) { + defer w.Done() + r := e - s + for i := range r { + if !y(a.Get(i+s), id) { + return } - wg.Wait() } } diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 6666c1a5..607d3265 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -35,11 +35,11 @@ func (m *PagedMap[K, V]) Get(key K) (value V, ok bool) { if pageID >= len(m.book) { return value, false } - page := m.book[pageID] + page := &m.book[pageID] if page.data == nil { return value, false } - d := page.data[index] + d := &page.data[index] return d.value, d.ok } @@ -77,11 +77,7 @@ func (m *PagedMap[K, V]) Delete(key K) { } func (m *PagedMap[K, V]) getPageIDAndIndex(key K) (pageID int, index int) { - // Convert key to uint64 to handle large values safely - u := uint64(key) - pageID = int(u >> pageSizeShift) - index = int(u % pageSize) - return + return int(uint64(key) >> pageSizeShift), int(uint64(key) % pageSize) } func (m *PagedMap[K, V]) expandBook(minLen int) { diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 7dbb0e4d..8c03310f 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -49,7 +49,7 @@ type BoxCollider struct { AllowSleep bool } -func (c *BoxCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { +func (c *BoxCollider) GetSupport(direction vectors.Vec2, transform Transform2d) vectors.Vec2 { var maxDistance float32 = -math.MaxFloat32 var maxPoint vectors.Vec2 @@ -88,7 +88,7 @@ type CircleCollider struct { AllowSleep bool } -func (c *CircleCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { +func (c *CircleCollider) GetSupport(direction vectors.Vec2, transform Transform2d) vectors.Vec2 { angle := direction.Angle() rotatedRadius := vectors.Vec2{ X: c.Radius * float32(math.Cos(angle)), @@ -112,7 +112,7 @@ type PolygonCollider struct { AllowSleep bool } -func (c *PolygonCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { +func (c *PolygonCollider) GetSupport(direction vectors.Vec2, transform Transform2d) vectors.Vec2 { maxDot := math.Inf(-1) var maxVertex vectors.Vec2 diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index dc7c7c9c..b37a3767 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -27,12 +27,9 @@ import ( const debugTree = false -var maxNumWorkers = runtime.NumCPU() - 1 - func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { return CollisionDetectionBVHSystem{ activeCollisions: make(map[CollisionPair]ecs.Entity), - collisionEvents: make([]ecs.PagedArray[CollisionEvent], maxNumWorkers), trees: make([]bvh.Tree, 0, 8), treesLookup: make(map[stdcomponents.CollisionLayer]int, 8), } @@ -64,10 +61,11 @@ type CollisionDetectionBVHSystem struct { } func (s *CollisionDetectionBVHSystem) Init() { - for i := range maxNumWorkers { + s.numWorkers = runtime.NumCPU() - 2 + s.collisionEvents = make([]ecs.PagedArray[CollisionEvent], s.numWorkers) + for i := range s.numWorkers { s.collisionEvents[i] = ecs.NewPagedArray[CollisionEvent]() } - s.numWorkers = runtime.NumCPU() - 2 } func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { @@ -237,13 +235,13 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE colA := s.getGjkCollider(colliderA, entityA) colB := s.getGjkCollider(colliderB, entityB) // First detect collision using GJK - simplex, collision := gjk.CheckCollision(colA, colB, &transformA, &transformB) - if !collision { + test := gjk.New() + if !test.CheckCollision(colA, colB, transformA, transformB) { continue } // If collision detected, get penetration details using EPA - normal, depth := gjk.EPA(colA, colB, &transformA, &transformB, &simplex) + normal, depth := test.EPA(colA, colB, transformA, transformB) position := posA.XY.Add(posB.XY.Sub(posA.XY)) s.collisionEvents[workerId].Append(CollisionEvent{ entityA: entityA, diff --git a/stdsystems/collision-detection-grid.go b/stdsystems/collision-detection-grid.go index 496b963b..8f285aaf 100644 --- a/stdsystems/collision-detection-grid.go +++ b/stdsystems/collision-detection-grid.go @@ -152,7 +152,7 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { // Worker pool setup var wg sync.WaitGroup - maxNumWorkers := runtime.NumCPU() * 4 + maxNumWorkers := runtime.NumCPU() - 2 entitiesLength := len(entities) // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot entities numWorkers := max(min(entitiesLength/32, maxNumWorkers), 1) diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index ba67c8c5..9904535c 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -15,7 +15,6 @@ import ( "math" "runtime" "slices" - "sync" "time" ) @@ -63,11 +62,11 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { // Collect and sort render objects if cap(s.renderObjects) < s.RenderVisibles.Len() { - s.renderObjects = make([]renderObject, 0, s.RenderVisibles.Len()) + s.renderObjects = make([]renderObject, 0, max(s.RenderVisibles.Len(), cap(s.renderObjects)*2)) } if cap(s.renderObjectsSorted) < s.RenderVisibles.Len() { - s.renderObjectsSorted = make([]renderObjectSorted, 0, s.RenderVisibles.Len()) + s.renderObjectsSorted = make([]renderObjectSorted, 0, max(s.RenderVisibles.Len(), cap(s.renderObjects)*2)) } s.RenderVisibles.EachEntity()(func(entity ecs.Entity) bool { @@ -140,19 +139,15 @@ func (s *Render2DCamerasSystem) Destroy() { } func (s *Render2DCamerasSystem) prepareRender(dt time.Duration) { - wg := new(sync.WaitGroup) - wg.Add(6) - s.prepareAnimations(wg) - go s.prepareFlips(wg) - go s.preparePositions(wg, dt) - go s.prepareRotations(wg) - go s.prepareScales(wg) - go s.prepareTints(wg) - wg.Wait() + s.prepareAnimations() + s.prepareFlips() + s.preparePositions(dt) + s.prepareRotations() + s.prepareScales() + s.prepareTints() } -func (s *Render2DCamerasSystem) prepareAnimations(wg *sync.WaitGroup) { - defer wg.Done() +func (s *Render2DCamerasSystem) prepareAnimations() { s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) animation := s.AnimationPlayers.GetUnsafe(entity) @@ -169,8 +164,7 @@ func (s *Render2DCamerasSystem) prepareAnimations(wg *sync.WaitGroup) { }) } -func (s *Render2DCamerasSystem) prepareFlips(wg *sync.WaitGroup) { - defer wg.Done() +func (s *Render2DCamerasSystem) prepareFlips() { s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) flipped := s.Flips.GetUnsafe(entity) @@ -187,8 +181,7 @@ func (s *Render2DCamerasSystem) prepareFlips(wg *sync.WaitGroup) { }) } -func (s *Render2DCamerasSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { - defer wg.Done() +func (s *Render2DCamerasSystem) preparePositions(dt time.Duration) { //dts := dt.Seconds() s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) @@ -206,8 +199,7 @@ func (s *Render2DCamerasSystem) preparePositions(wg *sync.WaitGroup, dt time.Dur }) } -func (s *Render2DCamerasSystem) prepareRotations(wg *sync.WaitGroup) { - defer wg.Done() +func (s *Render2DCamerasSystem) prepareRotations() { s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) rotation := s.Rotations.GetUnsafe(entity) @@ -219,8 +211,7 @@ func (s *Render2DCamerasSystem) prepareRotations(wg *sync.WaitGroup) { }) } -func (s *Render2DCamerasSystem) prepareScales(wg *sync.WaitGroup) { - defer wg.Done() +func (s *Render2DCamerasSystem) prepareScales() { s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { texturePro := s.Textures.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) @@ -233,8 +224,7 @@ func (s *Render2DCamerasSystem) prepareScales(wg *sync.WaitGroup) { }) } -func (s *Render2DCamerasSystem) prepareTints(wg *sync.WaitGroup) { - defer wg.Done() +func (s *Render2DCamerasSystem) prepareTints() { s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { tr := s.Textures.GetUnsafe(entity) tint := s.Tints.GetUnsafe(entity) diff --git a/taskfile.yml b/taskfile.yml index 8870a661..d9bab054 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -60,7 +60,7 @@ tasks: env: CGO_ENABLED: 1 cmds: - - go build -o ./.dist/game-win64.exe -tags opengl43 examples/new-api/game.go + - go build -o ./.dist/game-win64.exe -tags opengl43 ./examples/new-api build-mac: - task: build-darwin-amd64 From 77d39266490bd3e06602a1b885ed4955c0202af5 Mon Sep 17 00:00:00 2001 From: bitver Date: Wed, 16 Apr 2025 01:59:51 +0500 Subject: [PATCH 140/196] +perf? --- stdsystems/collider.go | 55 +++++++++++++++++++++++++++--------------- stdsystems/culling.go | 41 +++++++++++++++++++------------ stdsystems/sprite.go | 26 +++++++++++++------- 3 files changed, 78 insertions(+), 44 deletions(-) diff --git a/stdsystems/collider.go b/stdsystems/collider.go index 822db91b..e74de62c 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -39,41 +39,60 @@ type ColliderSystem struct { ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager AABB *stdcomponents.AABBComponentManager - numWorkers int + numWorkers int + accAABB [][]ecs.Entity + accGenericColliders [][]ecs.Entity + accColliderSleepCreate [][]ecs.Entity + accColliderSleepDelete [][]ecs.Entity } func (s *ColliderSystem) Init() { s.numWorkers = runtime.NumCPU() - 2 + s.accAABB = make([][]ecs.Entity, s.numWorkers) + s.accGenericColliders = make([][]ecs.Entity, s.numWorkers) + + s.accColliderSleepCreate = make([][]ecs.Entity, s.numWorkers) + s.accColliderSleepDelete = make([][]ecs.Entity, s.numWorkers) } func (s *ColliderSystem) Run(dt time.Duration) { - var accAABB = make([][]ecs.Entity, s.numWorkers) - var accGenericColliders = make([][]ecs.Entity, s.numWorkers) + for i := range s.accAABB { + s.accAABB[i] = s.accAABB[i][:0] + } + for i := range s.accGenericColliders { + s.accGenericColliders[i] = s.accGenericColliders[i][:0] + } + for i := range s.accColliderSleepCreate { + s.accColliderSleepCreate[i] = s.accColliderSleepCreate[i][:0] + } + for i := range s.accColliderSleepDelete { + s.accColliderSleepDelete[i] = s.accColliderSleepDelete[i][:0] + } s.BoxColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { if !s.GenericColliders.Has(entity) { - accGenericColliders[workerId] = append(accGenericColliders[workerId], entity) + s.accGenericColliders[workerId] = append(s.accGenericColliders[workerId], entity) } if !s.AABB.Has(entity) { - accAABB[workerId] = append(accAABB[workerId], entity) + s.accAABB[workerId] = append(s.accAABB[workerId], entity) } return true }) s.CircleColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { if !s.GenericColliders.Has(entity) { - accGenericColliders[workerId] = append(accGenericColliders[workerId], entity) + s.accGenericColliders[workerId] = append(s.accGenericColliders[workerId], entity) } if !s.AABB.Has(entity) { - accAABB[workerId] = append(accAABB[workerId], entity) + s.accAABB[workerId] = append(s.accAABB[workerId], entity) } return true }) - for i := range accAABB { - a := accAABB[i] + for i := range s.accAABB { + a := s.accAABB[i] for _, entity := range a { s.AABB.Create(entity, stdcomponents.AABB{}) } } - for i := range accGenericColliders { - a := accGenericColliders[i] + for i := range s.accGenericColliders { + a := s.accGenericColliders[i] for _, entity := range a { s.GenericColliders.Create(entity, stdcomponents.GenericCollider{}) } @@ -167,8 +186,6 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - var accColliderSleepCreate = make([][]ecs.Entity, s.numWorkers) - var accColliderSleepDelete = make([][]ecs.Entity, s.numWorkers) s.GenericColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { genCollider := s.GenericColliders.GetUnsafe(entity) if genCollider.AllowSleep { @@ -182,24 +199,24 @@ func (s *ColliderSystem) Run(dt time.Duration) { isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(entity) if shouldSleep { if isSleeping == nil { - accColliderSleepCreate[workerId] = append(accColliderSleepCreate[workerId], entity) + s.accColliderSleepCreate[workerId] = append(s.accColliderSleepCreate[workerId], entity) } } else { if isSleeping != nil { - accColliderSleepDelete[workerId] = append(accColliderSleepDelete[workerId], entity) + s.accColliderSleepDelete[workerId] = append(s.accColliderSleepDelete[workerId], entity) } } } return true }) - for i := range accColliderSleepCreate { - a := accColliderSleepCreate[i] + for i := range s.accColliderSleepCreate { + a := s.accColliderSleepCreate[i] for _, entity := range a { s.ColliderSleepStateComponentManager.Create(entity, stdcomponents.ColliderSleepState{}) } } - for i := range accColliderSleepDelete { - a := accColliderSleepDelete[i] + for i := range s.accColliderSleepDelete { + a := s.accColliderSleepDelete[i] for _, entity := range a { s.ColliderSleepStateComponentManager.Delete(entity) } diff --git a/stdsystems/culling.go b/stdsystems/culling.go index 7006979c..57e8258b 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -20,21 +20,32 @@ func NewCullingSystem() CullingSystem { } type CullingSystem struct { - Renderables *stdcomponents.RenderableComponentManager - RenderVisible *stdcomponents.RenderVisibleComponentManager - RenderOrders *stdcomponents.RenderOrderComponentManager - Textures *stdcomponents.RLTextureProComponentManager - AABBs *stdcomponents.AABBComponentManager - Cameras *stdcomponents.CameraComponentManager - RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager - numWorkers int + Renderables *stdcomponents.RenderableComponentManager + RenderVisible *stdcomponents.RenderVisibleComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + Textures *stdcomponents.RLTextureProComponentManager + AABBs *stdcomponents.AABBComponentManager + Cameras *stdcomponents.CameraComponentManager + RenderTexture2D *stdcomponents.FrameBuffer2DComponentManager + numWorkers int + accRenderVisibleCreate [][]ecs.Entity + accRenderVisibleDelete [][]ecs.Entity } func (s *CullingSystem) Init() { s.numWorkers = runtime.NumCPU() - 2 + s.accRenderVisibleCreate = make([][]ecs.Entity, s.numWorkers) + s.accRenderVisibleDelete = make([][]ecs.Entity, s.numWorkers) } func (s *CullingSystem) Run(dt time.Duration) { + for i := range s.accRenderVisibleCreate { + s.accRenderVisibleCreate[i] = s.accRenderVisibleCreate[i][:0] + } + for i := range s.accRenderVisibleDelete { + s.accRenderVisibleDelete[i] = s.accRenderVisibleDelete[i][:0] + } + s.Renderables.EachComponentParallel(s.numWorkers)(func(r *stdcomponents.Renderable, i int) bool { r.Observed = false return true @@ -61,29 +72,27 @@ func (s *CullingSystem) Run(dt time.Duration) { return true }) - var accRenderVisibleCreate = make([][]ecs.Entity, s.numWorkers) - var accRenderVisibleDelete = make([][]ecs.Entity, s.numWorkers) s.Renderables.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { renderable := s.Renderables.GetUnsafe(entity) assert.NotNil(renderable) if !s.RenderVisible.Has(entity) { if renderable.Observed { - accRenderVisibleCreate[workerId] = append(accRenderVisibleCreate[workerId], entity) + s.accRenderVisibleCreate[workerId] = append(s.accRenderVisibleCreate[workerId], entity) } } else { if !renderable.Observed { - accRenderVisibleDelete[workerId] = append(accRenderVisibleDelete[workerId], entity) + s.accRenderVisibleDelete[workerId] = append(s.accRenderVisibleDelete[workerId], entity) } } return true }) - for a := range accRenderVisibleCreate { - for _, entity := range accRenderVisibleCreate[a] { + for a := range s.accRenderVisibleCreate { + for _, entity := range s.accRenderVisibleCreate[a] { s.RenderVisible.Create(entity, stdcomponents.RenderVisible{}) } } - for a := range accRenderVisibleDelete { - for _, entity := range accRenderVisibleDelete[a] { + for a := range s.accRenderVisibleDelete { + for _, entity := range s.accRenderVisibleDelete[a] { s.RenderVisible.Delete(entity) } } diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index 1d2c3048..3eafcb27 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -34,33 +34,41 @@ type SpriteSystem struct { RLTexturePros *stdcomponents.RLTextureProComponentManager RenderOrder *stdcomponents.RenderOrderComponentManager - numWorkers int + numWorkers int + accRenderOrder [][]ecs.Entity + accRLTexturePros [][]ecs.Entity } func (s *SpriteSystem) Init() { s.numWorkers = runtime.NumCPU() - 2 + s.accRenderOrder = make([][]ecs.Entity, s.numWorkers) + s.accRLTexturePros = make([][]ecs.Entity, s.numWorkers) } func (s *SpriteSystem) Run() { - var accRenderOrder = make([][]ecs.Entity, s.numWorkers) - var accRLTexturePros = make([][]ecs.Entity, s.numWorkers) + for i := range s.accRenderOrder { + s.accRenderOrder[i] = s.accRenderOrder[i][:0] + } + for i := range s.accRLTexturePros { + s.accRLTexturePros[i] = s.accRLTexturePros[i][:0] + } s.Sprites.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { renderOrder := s.RenderOrder.GetUnsafe(entity) if renderOrder == nil { - accRenderOrder[workerId] = append(accRenderOrder[workerId], entity) + s.accRenderOrder[workerId] = append(s.accRenderOrder[workerId], entity) } tr := s.RLTexturePros.GetUnsafe(entity) if tr == nil { - accRLTexturePros[workerId] = append(accRLTexturePros[workerId], entity) + s.accRLTexturePros[workerId] = append(s.accRLTexturePros[workerId], entity) } return true }) - for a := range accRenderOrder { - for _, entity := range accRenderOrder[a] { + for a := range s.accRenderOrder { + for _, entity := range s.accRenderOrder[a] { s.RenderOrder.Create(entity, stdcomponents.RenderOrder{}) } } - for a := range accRLTexturePros { - for _, entity := range accRLTexturePros[a] { + for a := range s.accRLTexturePros { + for _, entity := range s.accRLTexturePros[a] { s.RLTexturePros.Create(entity, stdcomponents.RLTexturePro{}) } } From bedf07eddd64fe39b91aaf56d8ce9a65b2eb14cc Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 16 Apr 2025 02:56:20 +0300 Subject: [PATCH 141/196] init pkg/worker - pool, task & worker --- pkg/worker/pool.go | 103 +++++++++++++++++++++++++++++++++++++++++++ pkg/worker/task.go | 26 +++++++++++ pkg/worker/worker.go | 87 ++++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 pkg/worker/pool.go create mode 100644 pkg/worker/task.go create mode 100644 pkg/worker/worker.go diff --git a/pkg/worker/pool.go b/pkg/worker/pool.go new file mode 100644 index 00000000..1386c105 --- /dev/null +++ b/pkg/worker/pool.go @@ -0,0 +1,103 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package worker + +import ( + "context" + "sync" +) + +func NewPool(n int) Pool { + return Pool{ + workers: make([]Worker, n), + taskChan: make(chan AnyTask), + errChan: make(chan TaskError), + } +} + +type Pool struct { + workers []Worker + wg sync.WaitGroup + ctx context.Context + ctxCancel context.CancelFunc + + // Cache + taskChan chan AnyTask + errChan chan TaskError +} + +func (p *Pool) Start() { + p.ctx, p.ctxCancel = context.WithCancel(context.Background()) + for i := range p.workers { + p.workers[i] = NewWorker(p.ctx, WorkerId(i)) + } + // + //p.wg.Add(len(p.workers)) + //for i := range p.workers { + // go p.workers[i].Run() + //} +} + +func (p *Pool) AddWorker() { + p.workers = append(p.workers, NewWorker(p.ctx, WorkerId(len(p.workers)))) + p.workers[len(p.workers)-1].Start(&p.wg) +} + +func (p *Pool) RemoveWorker() { + p.workers[len(p.workers)-1].Stop() + p.workers = p.workers[:len(p.workers)-1] +} + +func (p *Pool) Stop() { + p.ctxCancel() + p.wg.Wait() +} + +type poolJob struct { + taskChan <-chan AnyTask + errChan chan<- TaskError + ctx context.Context + wg *sync.WaitGroup +} + +func (p *Pool) ProcessJob(tasks []AnyTask) (err error) { + var wg sync.WaitGroup + var ctx, cancel = context.WithCancel(p.ctx) + defer cancel() + + var job = poolJob{ + taskChan: p.taskChan, + errChan: p.errChan, + ctx: ctx, + wg: &wg, + } + + for i := range p.workers { + p.workers[i].jobChan <- job + } + + wg.Add(len(tasks)) + for i := range tasks { + select { + case err = <-p.errChan: + return err + default: + p.taskChan <- tasks[i] + } + } + wg.Wait() + + return err +} diff --git a/pkg/worker/task.go b/pkg/worker/task.go new file mode 100644 index 00000000..04cf6b9e --- /dev/null +++ b/pkg/worker/task.go @@ -0,0 +1,26 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package worker + +import "context" + +type AnyTask interface { + Run(ctx context.Context, workerId WorkerId) error +} + +type TaskError struct { + Err error + Id WorkerId +} diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go new file mode 100644 index 00000000..9e973a08 --- /dev/null +++ b/pkg/worker/worker.go @@ -0,0 +1,87 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package worker + +import ( + "context" + "sync" +) + +type WorkerId int + +func NewWorker(ctx context.Context, id WorkerId) Worker { + return Worker{ + id: id, + ctx: ctx, + jobChan: make(chan poolJob), + } +} + +type Worker struct { + id WorkerId + ctx context.Context + jobChan chan poolJob + wg sync.WaitGroup +} + +func (w *Worker) Start(poolWg *sync.WaitGroup) { + poolWg.Add(1) + go w.run(poolWg) +} + +func (w *Worker) Stop() { + close(w.jobChan) +} + +func (w *Worker) run(poolWg *sync.WaitGroup) { + defer poolWg.Done() + for { + select { + case <-w.ctx.Done(): + return + case job, ok := <-w.jobChan: + if !ok { + return + } + w.processJob(job) + } + } +} + +func (w *Worker) processJob(job poolJob) { + for { + select { + case <-w.ctx.Done(): + return + case <-job.ctx.Done(): + return + case task := <-job.taskChan: + w.processTask(job, task) + } + } +} + +func (w *Worker) processTask(job poolJob, task AnyTask) { + defer job.wg.Done() + err := task.Run(job.ctx, w.id) + if err != nil { + job.errChan <- TaskError{Err: err, Id: w.id} + return + } +} + +func (w *Worker) Id() WorkerId { + return w.id +} From dea95442ddfee994100104f4f7d1f60a09fa0f97 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 17 Apr 2025 16:27:10 +0300 Subject: [PATCH 142/196] feat workers --- engine.go | 64 +-------------- examples/new-api/game.go | 5 +- examples/new-api/scenes/assterodd-scene.go | 3 +- examples/new-api/scenes/main-scene.go | 3 +- examples/new-api/systems/texture-circle.go | 8 +- examples/new-api/systems/texture-rect.go | 5 +- pkg/core/engine.go | 90 ++++++++++++++++++++++ game.go => pkg/core/game.go | 4 +- pkg/ecs/component-manager-shared.go | 5 +- pkg/ecs/component-manager.go | 5 +- pkg/ecs/paged-array.go | 65 +++++++++++----- pkg/ecs/world.go | 23 ++++-- pkg/worker/pool.go | 34 ++++---- pkg/worker/task.go | 4 + pkg/worker/worker.go | 32 +++++--- stdsystems/collider.go | 20 ++--- stdsystems/collision-detection-bvh.go | 7 +- stdsystems/collision-detection.go | 9 ++- stdsystems/culling.go | 10 ++- stdsystems/render-2d-cameras.go | 16 ++-- stdsystems/sprite.go | 10 ++- stdsystems/texture-position-smooth.go | 5 +- stdsystems/velocity.go | 5 +- 23 files changed, 271 insertions(+), 161 deletions(-) create mode 100644 pkg/core/engine.go rename game.go => pkg/core/game.go (93%) diff --git a/engine.go b/engine.go index c932b18d..0c305240 100644 --- a/engine.go +++ b/engine.go @@ -16,67 +16,9 @@ Thank you for your support! package gomp import ( - "log" - "time" + "gomp/pkg/core" ) -const ( - MaxFrameSkips = 5 -) - -func NewEngine(game AnyGame) Engine { - engine := Engine{ - Game: game, - } - - return engine -} - -type Engine struct { - Game AnyGame -} - -func (e *Engine) Run(tickrate uint, framerate uint) { - - fixedUpdDuration := time.Second / time.Duration(tickrate) - - var renderTicker *time.Ticker - if framerate > 0 { - renderTicker = time.NewTicker(time.Second / time.Duration(framerate)) - defer renderTicker.Stop() - } - - e.Game.Init() - defer e.Game.Destroy() - - var lastUpdateAt = time.Now() // TODO: REMOVE? - var nextFixedUpdateAt = time.Now() - var dt = time.Since(lastUpdateAt) - - for !e.Game.ShouldDestroy() { - if renderTicker != nil { - <-renderTicker.C - } - dt = time.Since(lastUpdateAt) - lastUpdateAt = time.Now() - - // Update - e.Game.Update(dt) - - // Fixed Update - loops := 0 - // TODO: Refactor to work without for loop - for nextFixedUpdateAt.Compare(time.Now()) == -1 && loops < MaxFrameSkips { - e.Game.FixedUpdate(fixedUpdDuration) - nextFixedUpdateAt = nextFixedUpdateAt.Add(fixedUpdDuration) - loops++ - } - if loops >= MaxFrameSkips { - nextFixedUpdateAt = time.Now() - log.Println("Too many updates detected") - } - - // RenderAssterodd - e.Game.Render(dt) - } +func NewEngine(game core.AnyGame) core.Engine { + return core.NewEngine(game) } diff --git a/examples/new-api/game.go b/examples/new-api/game.go index e5f19a71..0e8f1021 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -17,6 +17,7 @@ package main import ( "gomp" "gomp/examples/new-api/instances" + "gomp/pkg/core" "gomp/pkg/ecs" "gomp/stdsystems" "time" @@ -52,7 +53,7 @@ type Game struct { currentSceneId gomp.SceneId } -func (g *Game) Init() { +func (g *Game) Init(engine *core.Engine) { g.osHandlerSystem.Init() g.renderSystem.Init() @@ -61,7 +62,7 @@ func (g *Game) Init() { var world = &g.worlds[i] var systems = &world.Systems - world.Init() + world.Init(engine) systems.ColliderSystem.Init() systems.Velocity.Init() diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index 4575dfb0..e379a60c 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -20,6 +20,7 @@ package scenes import ( "gomp" "gomp/examples/new-api/instances" + "gomp/pkg/core" "gomp/pkg/ecs" "time" ) @@ -61,7 +62,7 @@ func NewAssteroddScene() AssteroddScene { } type AssteroddScene struct { - Game gomp.AnyGame + Game core.AnyGame World *instances.World } diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index e2b9d359..d81d1cc8 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -17,6 +17,7 @@ package scenes import ( "gomp" "gomp/examples/new-api/instances" + "gomp/pkg/core" "gomp/pkg/ecs" "time" ) @@ -26,7 +27,7 @@ func NewMainScene() MainScene { } type MainScene struct { - Game gomp.AnyGame + Game core.AnyGame World *instances.World } diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go index 2c9ccf3c..1416af1c 100644 --- a/examples/new-api/systems/texture-circle.go +++ b/examples/new-api/systems/texture-circle.go @@ -10,9 +10,10 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" "github.com/negrel/assert" "gomp/examples/new-api/components" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" - "runtime" "time" ) @@ -25,7 +26,7 @@ type TextureCircleSystem struct { Textures *stdcomponents.RLTextureProComponentManager texture rl.RenderTexture2D - numWorkers int + Engine *core.Engine } func (s *TextureCircleSystem) Init() { @@ -35,11 +36,10 @@ func (s *TextureCircleSystem) Init() { rl.DrawCircle(circleRadius, circleRadius, circleRadius, rl.White) rl.EndTextureMode() s.texture = texture - s.numWorkers = runtime.NumCPU() - 2 } func (s *TextureCircleSystem) Run(dt time.Duration) { - s.Circles.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, i int) bool { + s.Circles.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, _ worker.WorkerId) bool { circle := s.Circles.GetUnsafe(entity) assert.NotNil(circle, "circle is nil; entity: %d", entity) texture := s.Textures.GetUnsafe(entity) diff --git a/examples/new-api/systems/texture-rect.go b/examples/new-api/systems/texture-rect.go index 4de502df..ffeaa047 100644 --- a/examples/new-api/systems/texture-rect.go +++ b/examples/new-api/systems/texture-rect.go @@ -10,7 +10,9 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" "github.com/negrel/assert" "gomp/examples/new-api/components" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "runtime" "time" @@ -25,6 +27,7 @@ type TextureRectSystem struct { Textures *stdcomponents.RLTextureProComponentManager texture rl.RenderTexture2D numWorkers int + Engine *core.Engine } func (s *TextureRectSystem) Init() { @@ -38,7 +41,7 @@ func (s *TextureRectSystem) Init() { func (s *TextureRectSystem) Run(dt time.Duration) { // Create shallow copy of texture to draw rectangles - s.TextureRect.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, i int) bool { + s.TextureRect.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, i worker.WorkerId) bool { rect := s.TextureRect.GetUnsafe(entity) assert.NotNil(rect, "rect is nil; entity: %d", entity) texture := s.Textures.GetUnsafe(entity) diff --git a/pkg/core/engine.go b/pkg/core/engine.go new file mode 100644 index 00000000..09639818 --- /dev/null +++ b/pkg/core/engine.go @@ -0,0 +1,90 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package core + +import ( + "gomp/pkg/worker" + "log" + "runtime" + "time" +) + +const ( + MaxFrameSkips = 5 +) + +func NewEngine(game AnyGame) Engine { + numCpu := max(runtime.NumCPU(), 1) + engine := Engine{ + Game: game, + pool: worker.NewPool(numCpu), + } + return engine +} + +type Engine struct { + Game AnyGame + pool worker.Pool +} + +func (e *Engine) Run(tickrate uint, framerate uint) { + fixedUpdDuration := time.Second / time.Duration(tickrate) + + var renderTicker *time.Ticker + if framerate > 0 { + renderTicker = time.NewTicker(time.Second / time.Duration(framerate)) + defer renderTicker.Stop() + } + + e.Game.Init(e) + defer e.Game.Destroy() + + var lastUpdateAt = time.Now() // TODO: REMOVE? + var nextFixedUpdateAt = time.Now() + var dt = time.Since(lastUpdateAt) + + e.pool.Start() + + for !e.Game.ShouldDestroy() { + if renderTicker != nil { + <-renderTicker.C + } + dt = time.Since(lastUpdateAt) + lastUpdateAt = time.Now() + + // Update + e.Game.Update(dt) + + // Fixed Update + loops := 0 + // TODO: Refactor to work without for loop + for nextFixedUpdateAt.Compare(time.Now()) == -1 && loops < MaxFrameSkips { + e.Game.FixedUpdate(fixedUpdDuration) + nextFixedUpdateAt = nextFixedUpdateAt.Add(fixedUpdDuration) + loops++ + } + if loops >= MaxFrameSkips { + nextFixedUpdateAt = time.Now() + log.Println("Too many updates detected") + } + + // RenderAssterodd + e.Game.Render(dt) + } +} + +func (e *Engine) Pool() *worker.Pool { + return &e.pool +} diff --git a/game.go b/pkg/core/game.go similarity index 93% rename from game.go rename to pkg/core/game.go index 3518e8fa..5930e721 100644 --- a/game.go +++ b/pkg/core/game.go @@ -12,14 +12,14 @@ none :) Thank you for your support! */ -package gomp +package core import ( "time" ) type AnyGame interface { - Init() + Init(engine *Engine) Update(dt time.Duration) FixedUpdate(dt time.Duration) Render(dt time.Duration) diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 822a6311..cd84ca02 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -7,6 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs import ( + "gomp/pkg/worker" "sync" "github.com/negrel/assert" @@ -250,10 +251,10 @@ func (c *SharedComponentManager[T]) EachComponentParallel(numWorkers int) func(y return c.components.EachDataParallel(numWorkers) } -func (c *SharedComponentManager[T]) EachEntityParallel(numWorkers int) func(yield func(Entity, int) bool) { +func (c *SharedComponentManager[T]) EachEntityParallel(pool *worker.Pool) func(yield func(Entity, worker.WorkerId) bool) { c.assertBegin() defer c.assertEnd() - return c.entities.EachDataValueParallel(numWorkers) + return c.entities.EachDataValueParallel(pool) } func (c *SharedComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entity, *T, int) bool) { diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 7c453e6d..6ed4fb15 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -17,6 +17,7 @@ package ecs import ( "github.com/negrel/assert" + "gomp/pkg/worker" "sync" ) @@ -268,10 +269,10 @@ func (c *ComponentManager[T]) EachComponentParallel(numWorkers int) func(yield f return c.components.EachDataParallel(numWorkers) } -func (c *ComponentManager[T]) EachEntityParallel(numWorkers int) func(yield func(Entity, int) bool) { +func (c *ComponentManager[T]) EachEntityParallel(pool *worker.Pool) func(yield func(Entity, worker.WorkerId) bool) { c.assertBegin() defer c.assertEnd() - return c.entities.EachDataValueParallel(numWorkers) + return c.entities.EachDataValueParallel(pool) } func (c *ComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entity, *T, int) bool) { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index fa62cdec..777cb177 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -7,9 +7,12 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs import ( - "sync" - + "context" + "errors" + "github.com/labstack/gommon/log" "github.com/negrel/assert" + "gomp/pkg/worker" + "sync" ) type PagedArray[T any] struct { @@ -17,10 +20,14 @@ type PagedArray[T any] struct { currentPageIndex int len int wg sync.WaitGroup + edvpTasks []EdvpTask[T] + tasks []worker.AnyTask } func NewPagedArray[T any]() (a PagedArray[T]) { a.book = make([]ArrayPage[T], 2, initialBookSize) + a.edvpTasks = make([]EdvpTask[T], initialBookSize) + a.tasks = make([]worker.AnyTask, initialBookSize) return a } @@ -68,6 +75,10 @@ func (a *PagedArray[T]) Append(values ...T) *T { if a.currentPageIndex >= len(a.book) { newBooks := make([]ArrayPage[T], len(a.book)*2) a.book = append(a.book, newBooks...) + newEdvpTasks := make([]EdvpTask[T], len(a.edvpTasks)*2) + a.edvpTasks = append(a.edvpTasks, newEdvpTasks...) + newTasks := make([]worker.AnyTask, len(a.tasks)*2) + a.tasks = append(a.tasks, newTasks...) } page := &a.book[a.currentPageIndex] @@ -77,6 +88,10 @@ func (a *PagedArray[T]) Append(values ...T) *T { if a.currentPageIndex >= len(a.book) { newBooks := make([]ArrayPage[T], len(a.book)*2) a.book = append(a.book, newBooks...) + newEdvpTasks := make([]EdvpTask[T], len(a.edvpTasks)*2) + a.edvpTasks = append(a.edvpTasks, newEdvpTasks...) + newTasks := make([]worker.AnyTask, len(a.tasks)*2) + a.tasks = append(a.tasks, newTasks...) } page = &a.book[a.currentPageIndex] } @@ -252,30 +267,40 @@ func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { } } -func (a *PagedArray[T]) EachDataValueParallel(numWorkers int) func(yield func(T, int) bool) { - return func(yield func(T, int) bool) { - assert.True(numWorkers > 0) - var chunkSize = a.len / numWorkers +type EdvpTask[T any] struct { + f func(T, worker.WorkerId) bool + page *ArrayPage[T] +} - a.wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = a.len +func (t *EdvpTask[T]) Run(ctx context.Context, workerId worker.WorkerId) error { + for i := 0; i < t.page.len; i++ { + select { + case <-ctx.Done(): + return nil + default: + if shouldContinue := t.f(t.page.data[i], workerId); !shouldContinue { + return errors.New("loop canceled") } - go a.edvpTask(yield, startIndex, endIndex, workedId, &a.wg) } - a.wg.Wait() } + return nil } -func (a *PagedArray[T]) edvpTask(y func(T, int) bool, s int, e int, id int, w *sync.WaitGroup) { - defer w.Done() - r := e - s - for i := range r { - if !y(a.GetValue(i+s), id) { - return +func (a *PagedArray[T]) EachDataValueParallel(pool *worker.Pool) func(yield func(T, worker.WorkerId) bool) { + return func(yield func(T, worker.WorkerId) bool) { + assert.NotNil(pool) + for i := a.currentPageIndex; i >= 0; i-- { + j := a.currentPageIndex - i + a.edvpTasks[j] = EdvpTask[T]{ + f: yield, + page: &a.book[i], + } + a.tasks[j] = &a.edvpTasks[j] + } + tasks := a.tasks[:a.currentPageIndex+1] + err := pool.ProcessGroupTasks(tasks) + if err != nil { + log.Error(err) } } } diff --git a/pkg/ecs/world.go b/pkg/ecs/world.go index 4b45fdf8..a3cacc5c 100644 --- a/pkg/ecs/world.go +++ b/pkg/ecs/world.go @@ -15,22 +15,26 @@ Thank you for your support! package ecs import ( + "gomp/pkg/core" "reflect" ) type AnyWorld interface { - Init() + Init(engine *core.Engine) Destroy() injectEntityManagerToComponents() - injectComponentsToSystems() + injectToSystems() } type World[C, S any] struct { Entities EntityManager Components C Systems S + Engine *core.Engine } +var _ AnyWorld = new(World[any, any]) + func NewWorld[C AnyComponentList, S AnySystemList](componentList C, systemList S) World[C, S] { return World[C, S]{ Entities: NewEntityManager(), @@ -39,8 +43,10 @@ func NewWorld[C AnyComponentList, S AnySystemList](componentList C, systemList S } } -func (w *World[C, S]) Init() { - w.injectComponentsToSystems() +func (w *World[C, S]) Init(engine *core.Engine) { + w.Engine = engine + + w.injectToSystems() w.injectEntityManagerToComponents() w.Entities.init() } @@ -69,8 +75,7 @@ func (w *World[C, S]) injectEntityManagerToComponents() { } } -// injectToSystems -func (w *World[C, S]) injectComponentsToSystems() { +func (w *World[C, S]) injectToSystems() { systemList := &w.Systems componentList := &w.Components entityManager := &w.Entities @@ -82,6 +87,7 @@ func (w *World[C, S]) injectComponentsToSystems() { componentsLen := reflectedComponentList.NumField() entityManagerType := reflect.TypeOf(entityManager) + engineType := reflect.TypeOf(w.Engine) for i := range systemsLen { system := reflectedSystemList.Field(i) @@ -100,6 +106,11 @@ func (w *World[C, S]) injectComponentsToSystems() { continue } + if systemFieldType == engineType { + system.Field(j).Set(reflect.ValueOf(w.Engine)) + continue + } + // TODO: refactor to component list indexed map to speed up assignment for k := range componentsLen { component := reflectedComponentList.Field(k) diff --git a/pkg/worker/pool.go b/pkg/worker/pool.go index 1386c105..88af315c 100644 --- a/pkg/worker/pool.go +++ b/pkg/worker/pool.go @@ -34,20 +34,17 @@ type Pool struct { ctxCancel context.CancelFunc // Cache - taskChan chan AnyTask - errChan chan TaskError + taskChan chan AnyTask + errChan chan TaskError + groupTaskWg sync.WaitGroup } func (p *Pool) Start() { p.ctx, p.ctxCancel = context.WithCancel(context.Background()) for i := range p.workers { p.workers[i] = NewWorker(p.ctx, WorkerId(i)) + p.workers[i].Start(&p.wg) } - // - //p.wg.Add(len(p.workers)) - //for i := range p.workers { - // go p.workers[i].Run() - //} } func (p *Pool) AddWorker() { @@ -65,39 +62,42 @@ func (p *Pool) Stop() { p.wg.Wait() } -type poolJob struct { +type groupTasks struct { taskChan <-chan AnyTask errChan chan<- TaskError ctx context.Context wg *sync.WaitGroup } -func (p *Pool) ProcessJob(tasks []AnyTask) (err error) { - var wg sync.WaitGroup +func (p *Pool) ProcessGroupTasks(tasks []AnyTask) error { var ctx, cancel = context.WithCancel(p.ctx) defer cancel() - var job = poolJob{ + var job = groupTasks{ taskChan: p.taskChan, errChan: p.errChan, ctx: ctx, - wg: &wg, + wg: &p.groupTaskWg, } for i := range p.workers { - p.workers[i].jobChan <- job + p.workers[i].groupTasksChan <- job } - wg.Add(len(tasks)) + p.groupTaskWg.Add(len(tasks)) for i := range tasks { select { - case err = <-p.errChan: + case err := <-p.errChan: return err default: p.taskChan <- tasks[i] } } - wg.Wait() + p.groupTaskWg.Wait() - return err + return nil +} + +func (p *Pool) NumWorkers() int { + return len(p.workers) } diff --git a/pkg/worker/task.go b/pkg/worker/task.go index 04cf6b9e..8c04a4f4 100644 --- a/pkg/worker/task.go +++ b/pkg/worker/task.go @@ -24,3 +24,7 @@ type TaskError struct { Err error Id WorkerId } + +func (e TaskError) Error() string { + return e.Err.Error() +} diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go index 9e973a08..218ed3ad 100644 --- a/pkg/worker/worker.go +++ b/pkg/worker/worker.go @@ -16,6 +16,8 @@ package worker import ( "context" + "github.com/negrel/assert" + "runtime" "sync" ) @@ -23,17 +25,17 @@ type WorkerId int func NewWorker(ctx context.Context, id WorkerId) Worker { return Worker{ - id: id, - ctx: ctx, - jobChan: make(chan poolJob), + id: id, + ctx: ctx, + groupTasksChan: make(chan groupTasks), } } type Worker struct { - id WorkerId - ctx context.Context - jobChan chan poolJob - wg sync.WaitGroup + id WorkerId + ctx context.Context + groupTasksChan chan groupTasks + wg sync.WaitGroup } func (w *Worker) Start(poolWg *sync.WaitGroup) { @@ -42,25 +44,28 @@ func (w *Worker) Start(poolWg *sync.WaitGroup) { } func (w *Worker) Stop() { - close(w.jobChan) + close(w.groupTasksChan) } func (w *Worker) run(poolWg *sync.WaitGroup) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + defer poolWg.Done() for { select { case <-w.ctx.Done(): return - case job, ok := <-w.jobChan: + case job, ok := <-w.groupTasksChan: if !ok { return } - w.processJob(job) + w.processGroupTasks(job) } } } -func (w *Worker) processJob(job poolJob) { +func (w *Worker) processGroupTasks(job groupTasks) { for { select { case <-w.ctx.Done(): @@ -73,8 +78,11 @@ func (w *Worker) processJob(job poolJob) { } } -func (w *Worker) processTask(job poolJob, task AnyTask) { +func (w *Worker) processTask(job groupTasks, task AnyTask) { defer job.wg.Done() + assert.NotNil(job.ctx) + assert.NotNil(task) + err := task.Run(job.ctx, w.id) if err != nil { job.errChan <- TaskError{Err: err, Id: w.id} diff --git a/stdsystems/collider.go b/stdsystems/collider.go index e74de62c..0ee65e7e 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -16,10 +16,11 @@ package stdsystems import ( "github.com/negrel/assert" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" - "runtime" "time" ) @@ -44,10 +45,11 @@ type ColliderSystem struct { accGenericColliders [][]ecs.Entity accColliderSleepCreate [][]ecs.Entity accColliderSleepDelete [][]ecs.Entity + Engine *core.Engine } func (s *ColliderSystem) Init() { - s.numWorkers = runtime.NumCPU() - 2 + s.numWorkers = s.Engine.Pool().NumWorkers() s.accAABB = make([][]ecs.Entity, s.numWorkers) s.accGenericColliders = make([][]ecs.Entity, s.numWorkers) @@ -67,7 +69,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { for i := range s.accColliderSleepDelete { s.accColliderSleepDelete[i] = s.accColliderSleepDelete[i][:0] } - s.BoxColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.BoxColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { if !s.GenericColliders.Has(entity) { s.accGenericColliders[workerId] = append(s.accGenericColliders[workerId], entity) } @@ -76,7 +78,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { } return true }) - s.CircleColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.CircleColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { if !s.GenericColliders.Has(entity) { s.accGenericColliders[workerId] = append(s.accGenericColliders[workerId], entity) } @@ -98,7 +100,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { } } - s.BoxColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + s.BoxColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { boxCollider := s.BoxColliders.GetUnsafe(entity) genCollider := s.GenericColliders.GetUnsafe(entity) @@ -113,7 +115,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.BoxColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + s.BoxColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { boxCollider := s.BoxColliders.GetUnsafe(entity) assert.NotNil(boxCollider) @@ -148,7 +150,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.CircleColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + s.CircleColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { circleCollider := s.CircleColliders.GetUnsafe(entity) assert.NotNil(circleCollider) @@ -165,7 +167,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.CircleColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + s.CircleColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { circleCollider := s.CircleColliders.GetUnsafe(entity) assert.NotNil(circleCollider) @@ -186,7 +188,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { return true }) - s.GenericColliders.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.GenericColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { genCollider := s.GenericColliders.GetUnsafe(entity) if genCollider.AllowSleep { shouldSleep := true diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index b37a3767..a239c7ca 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -17,7 +17,9 @@ package stdsystems import ( "gomp/pkg/bvh" gjk "gomp/pkg/collision" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" "runtime" @@ -58,6 +60,7 @@ type CollisionDetectionBVHSystem struct { activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities currentCollisions map[CollisionPair]struct{} numWorkers int + Engine *core.Engine } func (s *CollisionDetectionBVHSystem) Init() { @@ -111,7 +114,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { func (s *CollisionDetectionBVHSystem) Destroy() {} func (s *CollisionDetectionBVHSystem) findEntityCollisions() { - s.GenericCollider.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.GenericCollider.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { potentialEntities := s.broadPhase(entity, make([]ecs.Entity, 0, 64)) if len(potentialEntities) == 0 { return true @@ -189,7 +192,7 @@ func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, result []ec return result } -func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialEntities []ecs.Entity, workerId int) { +func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialEntities []ecs.Entity, workerId worker.WorkerId) { for _, entityB := range potentialEntities { if entityA == entityB { continue diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index a98fcc1d..14f694bf 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -16,7 +16,9 @@ package stdsystems import ( "github.com/negrel/assert" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" "image/color" @@ -50,6 +52,7 @@ type CollisionDetectionSystem struct { gridLookup map[stdcomponents.CollisionLayer]ecs.Entity numWorkers int + Engine *core.Engine } func (s *CollisionDetectionSystem) Init() { @@ -63,7 +66,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { func (s *CollisionDetectionSystem) Destroy() {} func (s *CollisionDetectionSystem) setup() { // Reset grids - s.CollisionGridComponentManager.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.CollisionGridComponentManager.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { grid := s.CollisionGridComponentManager.GetUnsafe(entity) assert.NotNil(grid) grid.Entities.Reset() @@ -76,7 +79,7 @@ func (s *CollisionDetectionSystem) setup() { // Accumulate used CollisionLayers var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, s.numWorkers) - s.GenericCollider.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.GenericCollider.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { collider := s.GenericCollider.GetUnsafe(entity) assert.NotNil(collider) collisionLayerAccumulators[workerId] |= 1 << collider.Layer @@ -194,7 +197,7 @@ func (s *CollisionDetectionSystem) setup() { return true }) - s.CollisionChunkComponentManager.EachEntityParallel(s.numWorkers)(func(chunkEntity ecs.Entity, workerId int) bool { + s.CollisionChunkComponentManager.EachEntityParallel(s.Engine.Pool())(func(chunkEntity ecs.Entity, workerId worker.WorkerId) bool { tree := s.BvhTreeComponentManager.GetUnsafe(chunkEntity) assert.NotNil(tree) tree.Build() diff --git a/stdsystems/culling.go b/stdsystems/culling.go index 57e8258b..25a89a96 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -8,10 +8,11 @@ package stdsystems import ( "github.com/negrel/assert" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" - "runtime" "time" ) @@ -30,10 +31,11 @@ type CullingSystem struct { numWorkers int accRenderVisibleCreate [][]ecs.Entity accRenderVisibleDelete [][]ecs.Entity + Engine *core.Engine } func (s *CullingSystem) Init() { - s.numWorkers = runtime.NumCPU() - 2 + s.numWorkers = s.Engine.Pool().NumWorkers() s.accRenderVisibleCreate = make([][]ecs.Entity, s.numWorkers) s.accRenderVisibleDelete = make([][]ecs.Entity, s.numWorkers) } @@ -55,7 +57,7 @@ func (s *CullingSystem) Run(dt time.Duration) { camera := s.Cameras.GetUnsafe(entity) cameraRect := camera.Rect() - s.Renderables.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + s.Renderables.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { renderable := s.Renderables.GetUnsafe(entity) assert.NotNil(renderable) @@ -72,7 +74,7 @@ func (s *CullingSystem) Run(dt time.Duration) { return true }) - s.Renderables.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.Renderables.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { renderable := s.Renderables.GetUnsafe(entity) assert.NotNil(renderable) if !s.RenderVisible.Has(entity) { diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index 9904535c..e4f08909 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -10,7 +10,9 @@ import ( "cmp" rl "github.com/gen2brain/raylib-go/raylib" "github.com/negrel/assert" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "math" "runtime" @@ -39,6 +41,8 @@ type Render2DCamerasSystem struct { renderObjects []renderObject renderObjectsSorted []renderObjectSorted numWorkers int + + Engine *core.Engine } type renderObject struct { @@ -148,7 +152,7 @@ func (s *Render2DCamerasSystem) prepareRender(dt time.Duration) { } func (s *Render2DCamerasSystem) prepareAnimations() { - s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { texturePro := s.Textures.GetUnsafe(entity) animation := s.AnimationPlayers.GetUnsafe(entity) if animation == nil { @@ -165,7 +169,7 @@ func (s *Render2DCamerasSystem) prepareAnimations() { } func (s *Render2DCamerasSystem) prepareFlips() { - s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { texturePro := s.Textures.GetUnsafe(entity) flipped := s.Flips.GetUnsafe(entity) if flipped == nil { @@ -183,7 +187,7 @@ func (s *Render2DCamerasSystem) prepareFlips() { func (s *Render2DCamerasSystem) preparePositions(dt time.Duration) { //dts := dt.Seconds() - s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { texturePro := s.Textures.GetUnsafe(entity) position := s.Positions.GetUnsafe(entity) if position == nil { @@ -200,7 +204,7 @@ func (s *Render2DCamerasSystem) preparePositions(dt time.Duration) { } func (s *Render2DCamerasSystem) prepareRotations() { - s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { texturePro := s.Textures.GetUnsafe(entity) rotation := s.Rotations.GetUnsafe(entity) if rotation == nil { @@ -212,7 +216,7 @@ func (s *Render2DCamerasSystem) prepareRotations() { } func (s *Render2DCamerasSystem) prepareScales() { - s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { texturePro := s.Textures.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) if scale == nil { @@ -225,7 +229,7 @@ func (s *Render2DCamerasSystem) prepareScales() { } func (s *Render2DCamerasSystem) prepareTints() { - s.Textures.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { tr := s.Textures.GetUnsafe(entity) tint := s.Tints.GetUnsafe(entity) if tint == nil { diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index 3eafcb27..881c0fb8 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -17,9 +17,10 @@ package stdsystems import ( rl "github.com/gen2brain/raylib-go/raylib" "github.com/negrel/assert" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" - "runtime" ) func NewSpriteSystem() SpriteSystem { @@ -37,10 +38,11 @@ type SpriteSystem struct { numWorkers int accRenderOrder [][]ecs.Entity accRLTexturePros [][]ecs.Entity + Engine *core.Engine } func (s *SpriteSystem) Init() { - s.numWorkers = runtime.NumCPU() - 2 + s.numWorkers = s.Engine.Pool().NumWorkers() s.accRenderOrder = make([][]ecs.Entity, s.numWorkers) s.accRLTexturePros = make([][]ecs.Entity, s.numWorkers) } @@ -51,7 +53,7 @@ func (s *SpriteSystem) Run() { for i := range s.accRLTexturePros { s.accRLTexturePros[i] = s.accRLTexturePros[i][:0] } - s.Sprites.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, workerId int) bool { + s.Sprites.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { renderOrder := s.RenderOrder.GetUnsafe(entity) if renderOrder == nil { s.accRenderOrder[workerId] = append(s.accRenderOrder[workerId], entity) @@ -73,7 +75,7 @@ func (s *SpriteSystem) Run() { } } - s.Sprites.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, _ int) bool { + s.Sprites.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { sprite := s.Sprites.GetUnsafe(entity) assert.NotNil(sprite) diff --git a/stdsystems/texture-position-smooth.go b/stdsystems/texture-position-smooth.go index 5f40e114..9768ea53 100644 --- a/stdsystems/texture-position-smooth.go +++ b/stdsystems/texture-position-smooth.go @@ -9,7 +9,9 @@ package stdsystems import ( rl "github.com/gen2brain/raylib-go/raylib" "github.com/negrel/assert" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" "math" @@ -26,6 +28,7 @@ type TexturePositionSmoothSystem struct { Position *stdcomponents.PositionComponentManager RLTexture *stdcomponents.RLTextureProComponentManager numWorkers int + Engine *core.Engine } func (s *TexturePositionSmoothSystem) Init() { @@ -54,7 +57,7 @@ func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { } //END DEBUG - s.TexturePositionSmooth.EachEntityParallel(s.numWorkers)(func(entity ecs.Entity, i int) bool { + s.TexturePositionSmooth.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, i worker.WorkerId) bool { position := s.Position.GetUnsafe(entity) texture := s.RLTexture.GetUnsafe(entity) smooth := s.TexturePositionSmooth.GetUnsafe(entity) diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 2b57805c..1637f599 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -8,7 +8,9 @@ package stdsystems import ( "github.com/negrel/assert" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "math" "runtime" @@ -25,6 +27,7 @@ type VelocitySystem struct { RigidBodies *stdcomponents.RigidBodyComponentManager numWorkers int + Engine *core.Engine } func (s *VelocitySystem) Init() { @@ -34,7 +37,7 @@ func (s *VelocitySystem) Init() { func (s *VelocitySystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - s.Velocities.EachEntityParallel(s.numWorkers)(func(e ecs.Entity, _ int) bool { + s.Velocities.EachEntityParallel(s.Engine.Pool())(func(e ecs.Entity, workerId worker.WorkerId) bool { velocity := s.Velocities.GetUnsafe(e) assert.True(s.isVelocityValid(velocity)) From 1c34304e9e3e6c613c8d9293fcfcf8a09e4206b7 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 17 Apr 2025 16:44:12 +0300 Subject: [PATCH 143/196] fix numworkers --- pkg/worker/worker.go | 4 ---- stdsystems/collision-detection-bvh.go | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go index 218ed3ad..1950a85c 100644 --- a/pkg/worker/worker.go +++ b/pkg/worker/worker.go @@ -17,7 +17,6 @@ package worker import ( "context" "github.com/negrel/assert" - "runtime" "sync" ) @@ -48,9 +47,6 @@ func (w *Worker) Stop() { } func (w *Worker) run(poolWg *sync.WaitGroup) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - defer poolWg.Done() for { select { diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index a239c7ca..14ba7f6b 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -22,7 +22,6 @@ import ( "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" - "runtime" "sync" "time" ) @@ -64,7 +63,7 @@ type CollisionDetectionBVHSystem struct { } func (s *CollisionDetectionBVHSystem) Init() { - s.numWorkers = runtime.NumCPU() - 2 + s.numWorkers = s.Engine.Pool().NumWorkers() s.collisionEvents = make([]ecs.PagedArray[CollisionEvent], s.numWorkers) for i := range s.numWorkers { s.collisionEvents[i] = ecs.NewPagedArray[CollisionEvent]() From 9123a49433c10836ec2a274a1e0fa22d04069dfd Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 17 Apr 2025 22:21:15 +0300 Subject: [PATCH 144/196] update worker pool performance --- pkg/core/engine.go | 2 +- pkg/ecs/component-manager-shared.go | 4 +- pkg/ecs/component-manager.go | 4 +- pkg/ecs/paged-array.go | 144 ++++++++++++++------------ pkg/ecs/paged-map.go | 2 +- pkg/worker/pool.go | 39 ++++--- stdsystems/collision-detection.go | 2 +- stdsystems/culling.go | 2 +- stdsystems/texture-position-smooth.go | 6 +- 9 files changed, 112 insertions(+), 93 deletions(-) diff --git a/pkg/core/engine.go b/pkg/core/engine.go index 09639818..a4b825d0 100644 --- a/pkg/core/engine.go +++ b/pkg/core/engine.go @@ -26,7 +26,7 @@ const ( ) func NewEngine(game AnyGame) Engine { - numCpu := max(runtime.NumCPU(), 1) + numCpu := max(runtime.NumCPU()-2, 1) engine := Engine{ Game: game, pool: worker.NewPool(numCpu), diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index cd84ca02..511fff8d 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -245,10 +245,10 @@ func (c *SharedComponentManager[T]) Each() func(yield func(Entity, *T) bool) { // Iterators Parallel // ======================================================== -func (c *SharedComponentManager[T]) EachComponentParallel(numWorkers int) func(yield func(*T, int) bool) { +func (c *SharedComponentManager[T]) EachComponentParallel(pool *worker.Pool) func(yield func(*T, worker.WorkerId) bool) { c.assertBegin() defer c.assertEnd() - return c.components.EachDataParallel(numWorkers) + return c.components.EachDataParallel(pool) } func (c *SharedComponentManager[T]) EachEntityParallel(pool *worker.Pool) func(yield func(Entity, worker.WorkerId) bool) { diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 6ed4fb15..faa1586e 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -263,10 +263,10 @@ func (c *ComponentManager[T]) Each() func(yield func(entity Entity, component *T // Iterators Parallel // ======================================================== -func (c *ComponentManager[T]) EachComponentParallel(numWorkers int) func(yield func(*T, int) bool) { +func (c *ComponentManager[T]) EachComponentParallel(pool *worker.Pool) func(yield func(*T, worker.WorkerId) bool) { c.assertBegin() defer c.assertEnd() - return c.components.EachDataParallel(numWorkers) + return c.components.EachDataParallel(pool) } func (c *ComponentManager[T]) EachEntityParallel(pool *worker.Pool) func(yield func(Entity, worker.WorkerId) bool) { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 777cb177..c72c57f5 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -15,21 +15,33 @@ import ( "sync" ) +func NewPagedArray[T any]() (a PagedArray[T]) { + a.book = make([]ArrayPage[T], 2, initialBookSize) + a.edpTasks = make([]EachDataTask[T], 2, initialBookSize) + a.edvpTasks = make([]EachDataValueTask[T], 2, initialBookSize) + + return a +} + +type SlicePage[T any] struct { + len int + data []T +} + type PagedArray[T any] struct { book []ArrayPage[T] currentPageIndex int len int wg sync.WaitGroup - edvpTasks []EdvpTask[T] - tasks []worker.AnyTask -} -func NewPagedArray[T any]() (a PagedArray[T]) { - a.book = make([]ArrayPage[T], 2, initialBookSize) - a.edvpTasks = make([]EdvpTask[T], initialBookSize) - a.tasks = make([]worker.AnyTask, initialBookSize) + // Cache + edvpTasks []EachDataValueTask[T] + edpTasks []EachDataTask[T] +} - return a +type ArrayPage[T any] struct { + len int + data [pageSize]T } func (a *PagedArray[T]) Len() int { @@ -75,10 +87,10 @@ func (a *PagedArray[T]) Append(values ...T) *T { if a.currentPageIndex >= len(a.book) { newBooks := make([]ArrayPage[T], len(a.book)*2) a.book = append(a.book, newBooks...) - newEdvpTasks := make([]EdvpTask[T], len(a.edvpTasks)*2) + newEdvpTasks := make([]EachDataValueTask[T], len(a.edvpTasks)*2) a.edvpTasks = append(a.edvpTasks, newEdvpTasks...) - newTasks := make([]worker.AnyTask, len(a.tasks)*2) - a.tasks = append(a.tasks, newTasks...) + newEdpTasks := make([]EachDataTask[T], len(a.edpTasks)*2) + a.edpTasks = append(a.edpTasks, newEdpTasks...) } page := &a.book[a.currentPageIndex] @@ -88,10 +100,10 @@ func (a *PagedArray[T]) Append(values ...T) *T { if a.currentPageIndex >= len(a.book) { newBooks := make([]ArrayPage[T], len(a.book)*2) a.book = append(a.book, newBooks...) - newEdvpTasks := make([]EdvpTask[T], len(a.edvpTasks)*2) + newEdvpTasks := make([]EachDataValueTask[T], len(a.edvpTasks)*2) a.edvpTasks = append(a.edvpTasks, newEdvpTasks...) - newTasks := make([]worker.AnyTask, len(a.tasks)*2) - a.tasks = append(a.tasks, newTasks...) + newEdpTasks := make([]EachDataTask[T], len(a.edpTasks)*2) + a.edpTasks = append(a.edpTasks, newEdpTasks...) } page = &a.book[a.currentPageIndex] } @@ -267,78 +279,80 @@ func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { } } -type EdvpTask[T any] struct { - f func(T, worker.WorkerId) bool - page *ArrayPage[T] -} +func (a *PagedArray[T]) EachDataValueParallel(pool *worker.Pool) func(yield func(T, worker.WorkerId) bool) { + return func(yield func(T, worker.WorkerId) bool) { + assert.NotNil(pool) -func (t *EdvpTask[T]) Run(ctx context.Context, workerId worker.WorkerId) error { - for i := 0; i < t.page.len; i++ { - select { - case <-ctx.Done(): - return nil - default: - if shouldContinue := t.f(t.page.data[i], workerId); !shouldContinue { - return errors.New("loop canceled") + pool.BeginGroupTasks() + for i := a.currentPageIndex; i >= 0; i-- { + j := a.currentPageIndex - i + a.edvpTasks[j].page = &a.book[i] + a.edvpTasks[j].f = yield + err := pool.ProcessGroupTask(&a.edvpTasks[j]) + if err != nil { + log.Error(err) } } + pool.EndGroupTask() } - return nil } -func (a *PagedArray[T]) EachDataValueParallel(pool *worker.Pool) func(yield func(T, worker.WorkerId) bool) { - return func(yield func(T, worker.WorkerId) bool) { +func (a *PagedArray[T]) EachDataParallel(pool *worker.Pool) func(yield func(*T, worker.WorkerId) bool) { + return func(yield func(*T, worker.WorkerId) bool) { assert.NotNil(pool) + + pool.BeginGroupTasks() for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i - a.edvpTasks[j] = EdvpTask[T]{ - f: yield, - page: &a.book[i], + a.edpTasks[j].page = &a.book[i] + a.edpTasks[j].f = yield + err := pool.ProcessGroupTask(&a.edpTasks[j]) + if err != nil { + log.Error(err) } - a.tasks[j] = &a.edvpTasks[j] - } - tasks := a.tasks[:a.currentPageIndex+1] - err := pool.ProcessGroupTasks(tasks) - if err != nil { - log.Error(err) } + pool.EndGroupTask() } } -func (a *PagedArray[T]) EachDataParallel(numWorkers int) func(yield func(*T, int) bool) { - return func(yield func(*T, int) bool) { - assert.True(numWorkers > 0) - var chunkSize = a.len / numWorkers +// ========================= +// TASKS +// ========================= - a.wg.Add(numWorkers) - for workedId := 0; workedId < numWorkers; workedId++ { - startIndex := workedId * chunkSize - endIndex := startIndex + chunkSize - 1 - if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker - endIndex = a.len - } - go a.edpTask(yield, startIndex, endIndex, workedId, &a.wg) - } - a.wg.Wait() - } +type EachDataValueTask[T any] struct { + f func(T, worker.WorkerId) bool + page *ArrayPage[T] } -func (a *PagedArray[T]) edpTask(y func(*T, int) bool, s int, e int, id int, w *sync.WaitGroup) { - defer w.Done() - r := e - s - for i := range r { - if !y(a.Get(i+s), id) { - return +func (t *EachDataValueTask[T]) Run(ctx context.Context, workerId worker.WorkerId) error { + for i := 0; i < t.page.len; i++ { + select { + case <-ctx.Done(): + return nil + default: + if shouldContinue := t.f(t.page.data[i], workerId); !shouldContinue { + return errors.New("loop canceled") + } } } + return nil } -type SlicePage[T any] struct { - len int - data []T +type EachDataTask[T any] struct { + f func(*T, worker.WorkerId) bool + page *ArrayPage[T] } -type ArrayPage[T any] struct { - len int - data [pageSize]T +func (t *EachDataTask[T]) Run(ctx context.Context, workerId worker.WorkerId) error { + for i := 0; i < t.page.len; i++ { + select { + case <-ctx.Done(): + return nil + default: + if shouldContinue := t.f(&t.page.data[i], workerId); !shouldContinue { + return errors.New("loop canceled") + } + } + } + return nil } diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 607d3265..2e00894e 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -7,7 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs const ( - pageSizeShift = 10 + pageSizeShift = 11 pageSize = 1 << pageSizeShift initialBookSize = 4 // Starting with a small initial book size ) diff --git a/pkg/worker/pool.go b/pkg/worker/pool.go index 88af315c..60407142 100644 --- a/pkg/worker/pool.go +++ b/pkg/worker/pool.go @@ -22,7 +22,7 @@ import ( func NewPool(n int) Pool { return Pool{ workers: make([]Worker, n), - taskChan: make(chan AnyTask), + taskChan: make(chan AnyTask, n), errChan: make(chan TaskError), } } @@ -34,9 +34,11 @@ type Pool struct { ctxCancel context.CancelFunc // Cache - taskChan chan AnyTask - errChan chan TaskError - groupTaskWg sync.WaitGroup + taskChan chan AnyTask + errChan chan TaskError + groupTaskWg sync.WaitGroup + groupTaskCtx context.Context + groupTaskCtxCancel context.CancelFunc } func (p *Pool) Start() { @@ -69,35 +71,38 @@ type groupTasks struct { wg *sync.WaitGroup } -func (p *Pool) ProcessGroupTasks(tasks []AnyTask) error { - var ctx, cancel = context.WithCancel(p.ctx) - defer cancel() +func (p *Pool) BeginGroupTasks() { + p.groupTaskCtx, p.groupTaskCtxCancel = context.WithCancel(p.ctx) var job = groupTasks{ taskChan: p.taskChan, errChan: p.errChan, - ctx: ctx, + ctx: p.groupTaskCtx, wg: &p.groupTaskWg, } for i := range p.workers { p.workers[i].groupTasksChan <- job } +} - p.groupTaskWg.Add(len(tasks)) - for i := range tasks { - select { - case err := <-p.errChan: - return err - default: - p.taskChan <- tasks[i] - } +func (p *Pool) ProcessGroupTask(task AnyTask) error { + p.groupTaskWg.Add(1) + select { + case err := <-p.errChan: + return err + default: + p.taskChan <- task } - p.groupTaskWg.Wait() return nil } +func (p *Pool) EndGroupTask() { + defer p.groupTaskCtxCancel() + p.groupTaskWg.Wait() +} + func (p *Pool) NumWorkers() int { return len(p.workers) } diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 14f694bf..ff0060d7 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -78,7 +78,7 @@ func (s *CollisionDetectionSystem) setup() { }) // Accumulate used CollisionLayers - var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, s.numWorkers) + var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, s.Engine.Pool().NumWorkers()) s.GenericCollider.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { collider := s.GenericCollider.GetUnsafe(entity) assert.NotNil(collider) diff --git a/stdsystems/culling.go b/stdsystems/culling.go index 25a89a96..733783b8 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -48,7 +48,7 @@ func (s *CullingSystem) Run(dt time.Duration) { s.accRenderVisibleDelete[i] = s.accRenderVisibleDelete[i][:0] } - s.Renderables.EachComponentParallel(s.numWorkers)(func(r *stdcomponents.Renderable, i int) bool { + s.Renderables.EachComponentParallel(s.Engine.Pool())(func(r *stdcomponents.Renderable, i worker.WorkerId) bool { r.Observed = false return true }) diff --git a/stdsystems/texture-position-smooth.go b/stdsystems/texture-position-smooth.go index 9768ea53..6e55e75f 100644 --- a/stdsystems/texture-position-smooth.go +++ b/stdsystems/texture-position-smooth.go @@ -38,19 +38,19 @@ func (s *TexturePositionSmoothSystem) Init() { func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { //DEBUG Temporary, TODO: remove if rl.IsKeyPressed(rl.KeyI) { - s.TexturePositionSmooth.EachComponentParallel(s.numWorkers)(func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.Engine.Pool())(func(t *stdcomponents.TexturePositionSmooth, i worker.WorkerId) bool { *t = stdcomponents.TexturePositionSmoothOff return true }) } if rl.IsKeyPressed(rl.KeyO) { - s.TexturePositionSmooth.EachComponentParallel(s.numWorkers)(func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.Engine.Pool())(func(t *stdcomponents.TexturePositionSmooth, i worker.WorkerId) bool { *t = stdcomponents.TexturePositionSmoothLerp return true }) } if rl.IsKeyPressed(rl.KeyP) { - s.TexturePositionSmooth.EachComponentParallel(s.numWorkers)(func(t *stdcomponents.TexturePositionSmooth, i int) bool { + s.TexturePositionSmooth.EachComponentParallel(s.Engine.Pool())(func(t *stdcomponents.TexturePositionSmooth, i worker.WorkerId) bool { *t = stdcomponents.TexturePositionSmoothExpDecay return true }) From a869059ab53395ba3476c04bcc3ca2601ae4a4cf Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 19 Apr 2025 10:37:57 +0300 Subject: [PATCH 145/196] add donations --- pkg/core/engine.go | 2 +- pkg/worker/pool.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/core/engine.go b/pkg/core/engine.go index a4b825d0..2e06c2ef 100644 --- a/pkg/core/engine.go +++ b/pkg/core/engine.go @@ -7,7 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- тажефигня Donated 500 RUB Thank you for your support! */ diff --git a/pkg/worker/pool.go b/pkg/worker/pool.go index 60407142..ceb2e265 100644 --- a/pkg/worker/pool.go +++ b/pkg/worker/pool.go @@ -7,7 +7,12 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- Еблан Donated 228 RUB +<- VsehVertela Donated 500 RUB +<- Linkwayz Donated 500 RUB +<- thespacetime Donated 10 EUR +<- Linkwayz Donated 1500 RUB +<- mitwelve Donated 100 RUB Thank you for your support! */ From e61194c9916d7e1be63b8a2d39e26867eb496c30 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 19 Apr 2025 10:38:26 +0300 Subject: [PATCH 146/196] fmt --- pkg/ecs/component-manager.go | 2 +- stdsystems/render.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index faa1586e..fbeb5b6f 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -138,7 +138,7 @@ func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { } /* -GetUnsafe - is not thread safe. DO NOT store the pointer to the value anywhere, because it might be changed anytime with Create or Remove operations. +GetUnsafe - is not thread safe. DO NOT store the pointer to the value anywhere, because it might be changed anytime with Create or Delete operations. */ func (c *ComponentManager[T]) GetUnsafe(entity Entity) (component *T) { assert.True(c.isInitialized, "ComponentManager should be created with NewComponentManager()") diff --git a/stdsystems/render.go b/stdsystems/render.go index 0a84f380..281a9810 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -56,19 +56,19 @@ func (s *RenderSystem) Run(dt time.Duration) bool { rl.BeginDrawing() - for _, texture := range s.frames { - rl.BeginBlendMode(texture.BlendMode) - rl.DrawTexturePro(texture.Texture.Texture, + for _, frame := range s.frames { + rl.BeginBlendMode(frame.BlendMode) + rl.DrawTexturePro(frame.Texture.Texture, rl.Rectangle{ X: 0, Y: 0, - Width: float32(texture.Texture.Texture.Width), - Height: -float32(texture.Texture.Texture.Height), + Width: float32(frame.Texture.Texture.Width), + Height: -float32(frame.Texture.Texture.Height), }, - texture.Dst, + frame.Dst, rl.Vector2{}, - texture.Rotation, - texture.Tint, + frame.Rotation, + frame.Tint, ) rl.EndBlendMode() } From 7d654b5af17f8bb4d1176cf6b06df38b326ebc44 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 19 Apr 2025 20:42:15 +0300 Subject: [PATCH 147/196] update workers and pool --- asset-library.go | 6 +- examples/new-api/assets/assets.go | 2 - pkg/ecs/paged-array.go | 101 ++++++++++++------------------ pkg/worker/pool.go | 53 ++++------------ pkg/worker/task.go | 4 +- pkg/worker/worker.go | 56 +++++------------ stdsystems/debug.go | 7 +-- 7 files changed, 75 insertions(+), 154 deletions(-) diff --git a/asset-library.go b/asset-library.go index 8c972b9f..5465366d 100644 --- a/asset-library.go +++ b/asset-library.go @@ -7,8 +7,6 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package gomp import ( - "fmt" - "github.com/negrel/assert" ) @@ -47,7 +45,7 @@ func (r *AssetLibrary[T]) Get(path string) *T { func (r *AssetLibrary[T]) Load(path string) { _, ok := r.data[path] - assert.False(ok, fmt.Errorf("asset already loaded: %s", path)) + assert.False(ok, "asset already loaded") resource := r.loader(path) r.data[path] = &resource @@ -68,7 +66,7 @@ func (r *AssetLibrary[T]) LoadAll() { func (r *AssetLibrary[T]) Unload(path string) { value, ok := r.data[path] - assert.True(ok, fmt.Errorf("asset not loaded: %s", path)) + assert.True(ok, "asset not loaded") r.unloader(path, value) delete(r.data, path) } diff --git a/examples/new-api/assets/assets.go b/examples/new-api/assets/assets.go index 4cce7e3b..63ef3b75 100644 --- a/examples/new-api/assets/assets.go +++ b/examples/new-api/assets/assets.go @@ -8,7 +8,6 @@ package assets import ( "embed" - "fmt" "gomp" "image/png" "log" @@ -26,7 +25,6 @@ var Textures = gomp.CreateAssetLibrary( func(path string) rl.Texture2D { assert.True(rl.IsWindowReady(), "Window is not initialized") - fmt.Print() file, err := fs.Open(path) if err != nil { log.Panic("Error opening file") diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index c72c57f5..a90c2eec 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -7,9 +7,6 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs import ( - "context" - "errors" - "github.com/labstack/gommon/log" "github.com/negrel/assert" "gomp/pkg/worker" "sync" @@ -37,6 +34,7 @@ type PagedArray[T any] struct { // Cache edvpTasks []EachDataValueTask[T] edpTasks []EachDataTask[T] + pool *worker.Pool } type ArrayPage[T any] struct { @@ -80,17 +78,21 @@ func (a *PagedArray[T]) Set(index int, value T) *T { return &page.data[index] } +func (a *PagedArray[T]) extend() { + newBooks := make([]ArrayPage[T], len(a.book)*2) + a.book = append(a.book, newBooks...) + newEdvpTasks := make([]EachDataValueTask[T], len(a.edvpTasks)*2) + a.edvpTasks = append(a.edvpTasks, newEdvpTasks...) + newEdpTasks := make([]EachDataTask[T], len(a.edpTasks)*2) + a.edpTasks = append(a.edpTasks, newEdpTasks...) +} + func (a *PagedArray[T]) Append(values ...T) *T { var result *T for i := range values { value := values[i] if a.currentPageIndex >= len(a.book) { - newBooks := make([]ArrayPage[T], len(a.book)*2) - a.book = append(a.book, newBooks...) - newEdvpTasks := make([]EachDataValueTask[T], len(a.edvpTasks)*2) - a.edvpTasks = append(a.edvpTasks, newEdvpTasks...) - newEdpTasks := make([]EachDataTask[T], len(a.edpTasks)*2) - a.edpTasks = append(a.edpTasks, newEdpTasks...) + a.extend() } page := &a.book[a.currentPageIndex] @@ -98,12 +100,7 @@ func (a *PagedArray[T]) Append(values ...T) *T { if page.len == pageSize { a.currentPageIndex++ if a.currentPageIndex >= len(a.book) { - newBooks := make([]ArrayPage[T], len(a.book)*2) - a.book = append(a.book, newBooks...) - newEdvpTasks := make([]EachDataValueTask[T], len(a.edvpTasks)*2) - a.edvpTasks = append(a.edvpTasks, newEdvpTasks...) - newEdpTasks := make([]EachDataTask[T], len(a.edpTasks)*2) - a.edpTasks = append(a.edpTasks, newEdpTasks...) + a.extend() } page = &a.book[a.currentPageIndex] } @@ -176,7 +173,7 @@ func (a *PagedArray[T]) Raw(result []T) []T { result = result[:0] for i := 0; i <= a.currentPageIndex; i++ { page := &a.book[i] - result = append(result[:i*1024], append(result[i*1024:], page.data[:page.len]...)...) + result = append(result[:i*pageSize], append(result[i*pageSize:], page.data[:page.len]...)...) } return result @@ -280,39 +277,37 @@ func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { } func (a *PagedArray[T]) EachDataValueParallel(pool *worker.Pool) func(yield func(T, worker.WorkerId) bool) { - return func(yield func(T, worker.WorkerId) bool) { - assert.NotNil(pool) + a.pool = pool + return a.edvpIter +} - pool.BeginGroupTasks() - for i := a.currentPageIndex; i >= 0; i-- { - j := a.currentPageIndex - i - a.edvpTasks[j].page = &a.book[i] - a.edvpTasks[j].f = yield - err := pool.ProcessGroupTask(&a.edvpTasks[j]) - if err != nil { - log.Error(err) - } - } - pool.EndGroupTask() +func (a *PagedArray[T]) edvpIter(yield func(T, worker.WorkerId) bool) { + assert.NotNil(a.pool) + + for i := a.currentPageIndex; i >= 0; i-- { + j := a.currentPageIndex - i + a.edvpTasks[j].page = &a.book[i] + a.edvpTasks[j].f = yield + a.pool.ProcessGroupTask(&a.edvpTasks[j]) } + a.pool.GroupWait() } func (a *PagedArray[T]) EachDataParallel(pool *worker.Pool) func(yield func(*T, worker.WorkerId) bool) { - return func(yield func(*T, worker.WorkerId) bool) { - assert.NotNil(pool) + a.pool = pool + return a.edpIter +} - pool.BeginGroupTasks() - for i := a.currentPageIndex; i >= 0; i-- { - j := a.currentPageIndex - i - a.edpTasks[j].page = &a.book[i] - a.edpTasks[j].f = yield - err := pool.ProcessGroupTask(&a.edpTasks[j]) - if err != nil { - log.Error(err) - } - } - pool.EndGroupTask() +func (a *PagedArray[T]) edpIter(yield func(*T, worker.WorkerId) bool) { + assert.NotNil(a.pool) + + for i := a.currentPageIndex; i >= 0; i-- { + j := a.currentPageIndex - i + a.edpTasks[j].page = &a.book[i] + a.edpTasks[j].f = yield + a.pool.ProcessGroupTask(&a.edpTasks[j]) } + a.pool.GroupWait() } // ========================= @@ -324,16 +319,9 @@ type EachDataValueTask[T any] struct { page *ArrayPage[T] } -func (t *EachDataValueTask[T]) Run(ctx context.Context, workerId worker.WorkerId) error { +func (t *EachDataValueTask[T]) Run(workerId worker.WorkerId) error { for i := 0; i < t.page.len; i++ { - select { - case <-ctx.Done(): - return nil - default: - if shouldContinue := t.f(t.page.data[i], workerId); !shouldContinue { - return errors.New("loop canceled") - } - } + t.f(t.page.data[i], workerId) } return nil } @@ -343,16 +331,9 @@ type EachDataTask[T any] struct { page *ArrayPage[T] } -func (t *EachDataTask[T]) Run(ctx context.Context, workerId worker.WorkerId) error { +func (t *EachDataTask[T]) Run(workerId worker.WorkerId) error { for i := 0; i < t.page.len; i++ { - select { - case <-ctx.Done(): - return nil - default: - if shouldContinue := t.f(&t.page.data[i], workerId); !shouldContinue { - return errors.New("loop canceled") - } - } + t.f(&t.page.data[i], workerId) } return nil } diff --git a/pkg/worker/pool.go b/pkg/worker/pool.go index ceb2e265..1323fd0a 100644 --- a/pkg/worker/pool.go +++ b/pkg/worker/pool.go @@ -13,6 +13,7 @@ Donations during this file development: <- thespacetime Donated 10 EUR <- Linkwayz Donated 1500 RUB <- mitwelve Donated 100 RUB +<- tema881 Donated 100 RUB Thank you for your support! */ @@ -28,7 +29,6 @@ func NewPool(n int) Pool { return Pool{ workers: make([]Worker, n), taskChan: make(chan AnyTask, n), - errChan: make(chan TaskError), } } @@ -39,23 +39,22 @@ type Pool struct { ctxCancel context.CancelFunc // Cache - taskChan chan AnyTask - errChan chan TaskError - groupTaskWg sync.WaitGroup - groupTaskCtx context.Context - groupTaskCtxCancel context.CancelFunc + taskChan chan AnyTask + groupTaskWg *sync.WaitGroup } func (p *Pool) Start() { p.ctx, p.ctxCancel = context.WithCancel(context.Background()) + p.groupTaskWg = new(sync.WaitGroup) + p.wg = sync.WaitGroup{} for i := range p.workers { - p.workers[i] = NewWorker(p.ctx, WorkerId(i)) + p.workers[i] = NewWorker(p.ctx, WorkerId(i), p.taskChan, p.groupTaskWg) p.workers[i].Start(&p.wg) } } func (p *Pool) AddWorker() { - p.workers = append(p.workers, NewWorker(p.ctx, WorkerId(len(p.workers)))) + p.workers = append(p.workers, NewWorker(p.ctx, WorkerId(len(p.workers)), p.taskChan, p.groupTaskWg)) p.workers[len(p.workers)-1].Start(&p.wg) } @@ -69,43 +68,13 @@ func (p *Pool) Stop() { p.wg.Wait() } -type groupTasks struct { - taskChan <-chan AnyTask - errChan chan<- TaskError - ctx context.Context - wg *sync.WaitGroup -} - -func (p *Pool) BeginGroupTasks() { - p.groupTaskCtx, p.groupTaskCtxCancel = context.WithCancel(p.ctx) - - var job = groupTasks{ - taskChan: p.taskChan, - errChan: p.errChan, - ctx: p.groupTaskCtx, - wg: &p.groupTaskWg, - } - - for i := range p.workers { - p.workers[i].groupTasksChan <- job - } +func (p *Pool) GroupWait() { + p.groupTaskWg.Wait() } -func (p *Pool) ProcessGroupTask(task AnyTask) error { +func (p *Pool) ProcessGroupTask(tasks AnyTask) { p.groupTaskWg.Add(1) - select { - case err := <-p.errChan: - return err - default: - p.taskChan <- task - } - - return nil -} - -func (p *Pool) EndGroupTask() { - defer p.groupTaskCtxCancel() - p.groupTaskWg.Wait() + p.taskChan <- tasks } func (p *Pool) NumWorkers() int { diff --git a/pkg/worker/task.go b/pkg/worker/task.go index 8c04a4f4..8680ca54 100644 --- a/pkg/worker/task.go +++ b/pkg/worker/task.go @@ -14,10 +14,8 @@ Thank you for your support! package worker -import "context" - type AnyTask interface { - Run(ctx context.Context, workerId WorkerId) error + Run(workerId WorkerId) error } type TaskError struct { diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go index 1950a85c..7496970e 100644 --- a/pkg/worker/worker.go +++ b/pkg/worker/worker.go @@ -16,25 +16,26 @@ package worker import ( "context" - "github.com/negrel/assert" "sync" ) type WorkerId int -func NewWorker(ctx context.Context, id WorkerId) Worker { +func NewWorker(ctx context.Context, id WorkerId, taskChan <-chan AnyTask, taskWg *sync.WaitGroup) Worker { return Worker{ - id: id, - ctx: ctx, - groupTasksChan: make(chan groupTasks), + id: id, + ctx: ctx, + taskWg: taskWg, + taskChan: taskChan, } } type Worker struct { - id WorkerId - ctx context.Context - groupTasksChan chan groupTasks - wg sync.WaitGroup + id WorkerId + ctx context.Context + taskChan <-chan AnyTask + taskWg *sync.WaitGroup + wg sync.WaitGroup } func (w *Worker) Start(poolWg *sync.WaitGroup) { @@ -43,49 +44,26 @@ func (w *Worker) Start(poolWg *sync.WaitGroup) { } func (w *Worker) Stop() { - close(w.groupTasksChan) } func (w *Worker) run(poolWg *sync.WaitGroup) { + //runtime.LockOSThread() + //defer runtime.UnlockOSThread() defer poolWg.Done() for { select { case <-w.ctx.Done(): return - case job, ok := <-w.groupTasksChan: - if !ok { - return + case task := <-w.taskChan: + err := task.Run(w.id) + w.taskWg.Done() + if err != nil { + panic("not implemented") } - w.processGroupTasks(job) } } } -func (w *Worker) processGroupTasks(job groupTasks) { - for { - select { - case <-w.ctx.Done(): - return - case <-job.ctx.Done(): - return - case task := <-job.taskChan: - w.processTask(job, task) - } - } -} - -func (w *Worker) processTask(job groupTasks, task AnyTask) { - defer job.wg.Done() - assert.NotNil(job.ctx) - assert.NotNil(task) - - err := task.Run(job.ctx, w.id) - if err != nil { - job.errChan <- TaskError{Err: err, Id: w.id} - return - } -} - func (w *Worker) Id() WorkerId { return w.id } diff --git a/stdsystems/debug.go b/stdsystems/debug.go index e7b0610d..d0165aec 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -7,7 +7,6 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdsystems import ( - "fmt" "github.com/felixge/fgprof" rl "github.com/gen2brain/raylib-go/raylib" "log" @@ -40,7 +39,7 @@ func (s *DebugSystem) Run() { if rl.IsKeyPressed(rl.KeyF9) { if s.pprofEnabled { pprof.StopCPUProfile() - fmt.Println("CPU Profile Stopped") + log.Println("CPU Profile Stopped") // Create a memory profile file memProfileFile, err := os.Create("mem.out") @@ -53,7 +52,7 @@ func (s *DebugSystem) Run() { if err := pprof.WriteHeapProfile(memProfileFile); err != nil { panic(err) } - fmt.Println("Memory profile written to mem.prof") + log.Println("Memory profile written to mem.prof") } else { f, err := os.Create("cpu.out") if err != nil { @@ -64,7 +63,7 @@ func (s *DebugSystem) Run() { if err != nil { log.Fatal(err) } - fmt.Println("CPU Profile Started") + log.Println("CPU Profile Started") } s.pprofEnabled = !s.pprofEnabled From ff9bd06a2086fc045c6cbdc906b11fd6bb19c26f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 20 Apr 2025 13:57:57 +0300 Subject: [PATCH 148/196] breaking changes - iter EachParallel is now Process method --- examples/new-api/systems/texture-circle.go | 3 +- examples/new-api/systems/texture-rect.go | 3 +- pkg/ecs/component-manager-shared.go | 13 ++++++--- pkg/ecs/component-manager.go | 17 +++++++++--- pkg/ecs/paged-array.go | 23 ++++------------ stdsystems/collider.go | 25 +++++------------ stdsystems/collision-detection-bvh.go | 5 ++-- stdsystems/collision-detection.go | 9 ++---- stdsystems/culling.go | 9 ++---- stdsystems/render-2d-cameras.go | 32 +++++++++------------- stdsystems/sprite.go | 6 ++-- stdsystems/texture-position-smooth.go | 16 ++++------- stdsystems/velocity.go | 3 +- 13 files changed, 67 insertions(+), 97 deletions(-) diff --git a/examples/new-api/systems/texture-circle.go b/examples/new-api/systems/texture-circle.go index 1416af1c..23c1c6f7 100644 --- a/examples/new-api/systems/texture-circle.go +++ b/examples/new-api/systems/texture-circle.go @@ -39,7 +39,7 @@ func (s *TextureCircleSystem) Init() { } func (s *TextureCircleSystem) Run(dt time.Duration) { - s.Circles.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, _ worker.WorkerId) bool { + s.Circles.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { circle := s.Circles.GetUnsafe(entity) assert.NotNil(circle, "circle is nil; entity: %d", entity) texture := s.Textures.GetUnsafe(entity) @@ -60,7 +60,6 @@ func (s *TextureCircleSystem) Run(dt time.Duration) { texture.Origin.X = circle.Origin.X + circle.Radius texture.Origin.Y = circle.Origin.Y + circle.Radius texture.Tint = circle.Color - return true }) } diff --git a/examples/new-api/systems/texture-rect.go b/examples/new-api/systems/texture-rect.go index ffeaa047..b5a128d6 100644 --- a/examples/new-api/systems/texture-rect.go +++ b/examples/new-api/systems/texture-rect.go @@ -41,7 +41,7 @@ func (s *TextureRectSystem) Init() { func (s *TextureRectSystem) Run(dt time.Duration) { // Create shallow copy of texture to draw rectangles - s.TextureRect.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, i worker.WorkerId) bool { + s.TextureRect.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { rect := s.TextureRect.GetUnsafe(entity) assert.NotNil(rect, "rect is nil; entity: %d", entity) texture := s.Textures.GetUnsafe(entity) @@ -52,7 +52,6 @@ func (s *TextureRectSystem) Run(dt time.Duration) { texture.Rotation = rect.Rotation texture.Origin = rect.Origin texture.Tint = rect.Color - return true }) } diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 511fff8d..0e8c6c58 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -53,6 +53,7 @@ type SharedComponentManager[T any] struct { entityManager *EntityManager entityComponentBitSet *ComponentBitSet + pool *worker.Pool id ComponentId isInitialized bool @@ -68,6 +69,10 @@ type SharedComponentManager[T any] struct { decoder func([]byte) []T } +func (c *SharedComponentManager[T]) registerWorkerPool(pool *worker.Pool) { + c.pool = pool +} + func (c *SharedComponentManager[T]) PatchAdd(entity Entity) { //TODO implement me panic("implement me") @@ -245,16 +250,16 @@ func (c *SharedComponentManager[T]) Each() func(yield func(Entity, *T) bool) { // Iterators Parallel // ======================================================== -func (c *SharedComponentManager[T]) EachComponentParallel(pool *worker.Pool) func(yield func(*T, worker.WorkerId) bool) { +func (c *SharedComponentManager[T]) ProcessComponents(handler func(*T, worker.WorkerId)) { c.assertBegin() defer c.assertEnd() - return c.components.EachDataParallel(pool) + c.components.EachDataParallel(handler) } -func (c *SharedComponentManager[T]) EachEntityParallel(pool *worker.Pool) func(yield func(Entity, worker.WorkerId) bool) { +func (c *SharedComponentManager[T]) EachEntityParallel(handler func(Entity, worker.WorkerId)) { c.assertBegin() defer c.assertEnd() - return c.entities.EachDataValueParallel(pool) + c.entities.ProcessDataValue(handler) } func (c *SharedComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entity, *T, int) bool) { diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index fbeb5b6f..29c1d973 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -40,12 +40,15 @@ type AnyComponentManagerPtr interface { PatchReset() IsTrackingChanges() bool registerEntityManager(*EntityManager) + registerWorkerPool(*worker.Pool) } // ================ // Service // ================ +var _ AnyComponentManagerPtr = &ComponentManager[any]{} + func NewComponentManager[T any](id ComponentId) ComponentManager[T] { newManager := ComponentManager[T]{ components: NewPagedArray[T](), @@ -75,6 +78,8 @@ type ComponentManager[T any] struct { id ComponentId isInitialized bool + pool *worker.Pool + // Patch TrackChanges bool // Enable TrackChanges to track changes and add them to patch @@ -112,6 +117,10 @@ func (c *ComponentManager[T]) registerEntityManager(entityManager *EntityManager c.entityComponentBitSet = &entityManager.componentBitSet } +func (c *ComponentManager[T]) registerWorkerPool(pool *worker.Pool) { + c.pool = pool +} + //===================================== //===================================== //===================================== @@ -263,16 +272,16 @@ func (c *ComponentManager[T]) Each() func(yield func(entity Entity, component *T // Iterators Parallel // ======================================================== -func (c *ComponentManager[T]) EachComponentParallel(pool *worker.Pool) func(yield func(*T, worker.WorkerId) bool) { +func (c *ComponentManager[T]) ProcessEntities(handler func(Entity, worker.WorkerId)) { c.assertBegin() defer c.assertEnd() - return c.components.EachDataParallel(pool) + c.entities.ProcessDataValue(handler) } -func (c *ComponentManager[T]) EachEntityParallel(pool *worker.Pool) func(yield func(Entity, worker.WorkerId) bool) { +func (c *ComponentManager[T]) ProcessComponents(handler func(*T, worker.WorkerId)) { c.assertBegin() defer c.assertEnd() - return c.entities.EachDataValueParallel(pool) + c.components.EachDataParallel(handler) } func (c *ComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entity, *T, int) bool) { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index a90c2eec..cc3fa037 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -276,35 +276,24 @@ func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { } } -func (a *PagedArray[T]) EachDataValueParallel(pool *worker.Pool) func(yield func(T, worker.WorkerId) bool) { - a.pool = pool - return a.edvpIter -} - -func (a *PagedArray[T]) edvpIter(yield func(T, worker.WorkerId) bool) { +func (a *PagedArray[T]) ProcessDataValue(handler func(T, worker.WorkerId)) { assert.NotNil(a.pool) - for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i a.edvpTasks[j].page = &a.book[i] - a.edvpTasks[j].f = yield + a.edvpTasks[j].f = handler a.pool.ProcessGroupTask(&a.edvpTasks[j]) } a.pool.GroupWait() } -func (a *PagedArray[T]) EachDataParallel(pool *worker.Pool) func(yield func(*T, worker.WorkerId) bool) { - a.pool = pool - return a.edpIter -} - -func (a *PagedArray[T]) edpIter(yield func(*T, worker.WorkerId) bool) { +func (a *PagedArray[T]) EachDataParallel(handler func(*T, worker.WorkerId)) { assert.NotNil(a.pool) for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i a.edpTasks[j].page = &a.book[i] - a.edpTasks[j].f = yield + a.edpTasks[j].f = handler a.pool.ProcessGroupTask(&a.edpTasks[j]) } a.pool.GroupWait() @@ -315,7 +304,7 @@ func (a *PagedArray[T]) edpIter(yield func(*T, worker.WorkerId) bool) { // ========================= type EachDataValueTask[T any] struct { - f func(T, worker.WorkerId) bool + f func(T, worker.WorkerId) page *ArrayPage[T] } @@ -327,7 +316,7 @@ func (t *EachDataValueTask[T]) Run(workerId worker.WorkerId) error { } type EachDataTask[T any] struct { - f func(*T, worker.WorkerId) bool + f func(*T, worker.WorkerId) page *ArrayPage[T] } diff --git a/stdsystems/collider.go b/stdsystems/collider.go index 0ee65e7e..365c3771 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -69,23 +69,21 @@ func (s *ColliderSystem) Run(dt time.Duration) { for i := range s.accColliderSleepDelete { s.accColliderSleepDelete[i] = s.accColliderSleepDelete[i][:0] } - s.BoxColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.BoxColliders.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { if !s.GenericColliders.Has(entity) { s.accGenericColliders[workerId] = append(s.accGenericColliders[workerId], entity) } if !s.AABB.Has(entity) { s.accAABB[workerId] = append(s.accAABB[workerId], entity) } - return true }) - s.CircleColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.CircleColliders.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { if !s.GenericColliders.Has(entity) { s.accGenericColliders[workerId] = append(s.accGenericColliders[workerId], entity) } if !s.AABB.Has(entity) { s.accAABB[workerId] = append(s.accAABB[workerId], entity) } - return true }) for i := range s.accAABB { a := s.accAABB[i] @@ -100,7 +98,7 @@ func (s *ColliderSystem) Run(dt time.Duration) { } } - s.BoxColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.BoxColliders.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { boxCollider := s.BoxColliders.GetUnsafe(entity) genCollider := s.GenericColliders.GetUnsafe(entity) @@ -111,11 +109,9 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider.Offset.Y = boxCollider.Offset.Y genCollider.Shape = stdcomponents.BoxColliderShape genCollider.AllowSleep = boxCollider.AllowSleep - - return true }) - s.BoxColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.BoxColliders.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { boxCollider := s.BoxColliders.GetUnsafe(entity) assert.NotNil(boxCollider) @@ -146,11 +142,9 @@ func (s *ColliderSystem) Run(dt time.Duration) { aabb.Min = position.XY.Add(aabb.Min) aabb.Max = position.XY.Add(aabb.Max) - - return true }) - s.CircleColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.CircleColliders.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { circleCollider := s.CircleColliders.GetUnsafe(entity) assert.NotNil(circleCollider) @@ -163,11 +157,9 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider.Offset.Y = circleCollider.Offset.Y genCollider.Shape = stdcomponents.CircleColliderShape genCollider.AllowSleep = circleCollider.AllowSleep - - return true }) - s.CircleColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.CircleColliders.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { circleCollider := s.CircleColliders.GetUnsafe(entity) assert.NotNil(circleCollider) @@ -184,11 +176,9 @@ func (s *ColliderSystem) Run(dt time.Duration) { scaledRadius := scale.XY.Scale(circleCollider.Radius) aabb.Min = position.XY.Add(offset).Sub(scaledRadius) aabb.Max = position.XY.Add(offset).Add(scaledRadius) - - return true }) - s.GenericColliders.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.GenericColliders.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { genCollider := s.GenericColliders.GetUnsafe(entity) if genCollider.AllowSleep { shouldSleep := true @@ -209,7 +199,6 @@ func (s *ColliderSystem) Run(dt time.Duration) { } } } - return true }) for i := range s.accColliderSleepCreate { a := s.accColliderSleepCreate[i] diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 14ba7f6b..10cbfcff 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -113,14 +113,13 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { func (s *CollisionDetectionBVHSystem) Destroy() {} func (s *CollisionDetectionBVHSystem) findEntityCollisions() { - s.GenericCollider.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.GenericCollider.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { potentialEntities := s.broadPhase(entity, make([]ecs.Entity, 0, 64)) if len(potentialEntities) == 0 { - return true + return } s.narrowPhase(entity, potentialEntities, workerId) - return true }) } diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index ff0060d7..267aa8ad 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -66,7 +66,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { func (s *CollisionDetectionSystem) Destroy() {} func (s *CollisionDetectionSystem) setup() { // Reset grids - s.CollisionGridComponentManager.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.CollisionGridComponentManager.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { grid := s.CollisionGridComponentManager.GetUnsafe(entity) assert.NotNil(grid) grid.Entities.Reset() @@ -74,16 +74,14 @@ func (s *CollisionDetectionSystem) setup() { X: math.MaxFloat32, Y: math.MaxFloat32, } - return true }) // Accumulate used CollisionLayers var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, s.Engine.Pool().NumWorkers()) - s.GenericCollider.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.GenericCollider.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { collider := s.GenericCollider.GetUnsafe(entity) assert.NotNil(collider) collisionLayerAccumulators[workerId] |= 1 << collider.Layer - return true }) collisionLayerAccumulator := stdcomponents.CollisionLayer(0) for _, mask := range collisionLayerAccumulators { @@ -197,10 +195,9 @@ func (s *CollisionDetectionSystem) setup() { return true }) - s.CollisionChunkComponentManager.EachEntityParallel(s.Engine.Pool())(func(chunkEntity ecs.Entity, workerId worker.WorkerId) bool { + s.CollisionChunkComponentManager.ProcessEntities(func(chunkEntity ecs.Entity, workerId worker.WorkerId) { tree := s.BvhTreeComponentManager.GetUnsafe(chunkEntity) assert.NotNil(tree) tree.Build() - return true }) } diff --git a/stdsystems/culling.go b/stdsystems/culling.go index 733783b8..7db4aa7b 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -48,16 +48,15 @@ func (s *CullingSystem) Run(dt time.Duration) { s.accRenderVisibleDelete[i] = s.accRenderVisibleDelete[i][:0] } - s.Renderables.EachComponentParallel(s.Engine.Pool())(func(r *stdcomponents.Renderable, i worker.WorkerId) bool { + s.Renderables.ProcessComponents(func(r *stdcomponents.Renderable, workerId worker.WorkerId) { r.Observed = false - return true }) s.Cameras.EachEntity()(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) cameraRect := camera.Rect() - s.Renderables.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Renderables.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { renderable := s.Renderables.GetUnsafe(entity) assert.NotNil(renderable) @@ -69,12 +68,11 @@ func (s *CullingSystem) Run(dt time.Duration) { if s.intersects(cameraRect, textureRect) { renderable.Observed = true } - return true }) return true }) - s.Renderables.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Renderables.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { renderable := s.Renderables.GetUnsafe(entity) assert.NotNil(renderable) if !s.RenderVisible.Has(entity) { @@ -86,7 +84,6 @@ func (s *CullingSystem) Run(dt time.Duration) { s.accRenderVisibleDelete[workerId] = append(s.accRenderVisibleDelete[workerId], entity) } } - return true }) for a := range s.accRenderVisibleCreate { for _, entity := range s.accRenderVisibleCreate[a] { diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index e4f08909..6deede64 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -152,11 +152,12 @@ func (s *Render2DCamerasSystem) prepareRender(dt time.Duration) { } func (s *Render2DCamerasSystem) prepareAnimations() { - s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + // TODO revert loop to process animations but not a textures? + s.Textures.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { texturePro := s.Textures.GetUnsafe(entity) animation := s.AnimationPlayers.GetUnsafe(entity) if animation == nil { - return true + return } frame := &texturePro.Frame if animation.Vertical { @@ -164,16 +165,15 @@ func (s *Render2DCamerasSystem) prepareAnimations() { } else { frame.X += frame.Width * float32(animation.Current) } - return true }) } func (s *Render2DCamerasSystem) prepareFlips() { - s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Textures.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { texturePro := s.Textures.GetUnsafe(entity) flipped := s.Flips.GetUnsafe(entity) if flipped == nil { - return true + return } if flipped.X { texturePro.Frame.Width *= -1 @@ -181,66 +181,60 @@ func (s *Render2DCamerasSystem) prepareFlips() { if flipped.Y { texturePro.Frame.Height *= -1 } - return true }) } func (s *Render2DCamerasSystem) preparePositions(dt time.Duration) { //dts := dt.Seconds() - s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Textures.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { texturePro := s.Textures.GetUnsafe(entity) position := s.Positions.GetUnsafe(entity) if position == nil { - return true + return } //decay := 40.0 // DECAY IS TICKRATE DEPENDENT //x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) //y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) texturePro.Dest.X = position.XY.X texturePro.Dest.Y = position.XY.Y - - return true }) } func (s *Render2DCamerasSystem) prepareRotations() { - s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Textures.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { texturePro := s.Textures.GetUnsafe(entity) rotation := s.Rotations.GetUnsafe(entity) if rotation == nil { - return true + return } texturePro.Rotation = float32(rotation.Degrees()) - return true }) } func (s *Render2DCamerasSystem) prepareScales() { - s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Textures.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { texturePro := s.Textures.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) if scale == nil { - return true + return } texturePro.Dest.Width *= scale.XY.X texturePro.Dest.Height *= scale.XY.Y - return true }) } func (s *Render2DCamerasSystem) prepareTints() { - s.Textures.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Textures.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { tr := s.Textures.GetUnsafe(entity) tint := s.Tints.GetUnsafe(entity) if tint == nil { - return true + return } trTint := &tr.Tint trTint.A = tint.A trTint.R = tint.R trTint.G = tint.G trTint.B = tint.B - return true }) } diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index 881c0fb8..4eddcb6a 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -53,7 +53,7 @@ func (s *SpriteSystem) Run() { for i := range s.accRLTexturePros { s.accRLTexturePros[i] = s.accRLTexturePros[i][:0] } - s.Sprites.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Sprites.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { renderOrder := s.RenderOrder.GetUnsafe(entity) if renderOrder == nil { s.accRenderOrder[workerId] = append(s.accRenderOrder[workerId], entity) @@ -62,7 +62,6 @@ func (s *SpriteSystem) Run() { if tr == nil { s.accRLTexturePros[workerId] = append(s.accRLTexturePros[workerId], entity) } - return true }) for a := range s.accRenderOrder { for _, entity := range s.accRenderOrder[a] { @@ -75,7 +74,7 @@ func (s *SpriteSystem) Run() { } } - s.Sprites.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, workerId worker.WorkerId) bool { + s.Sprites.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { sprite := s.Sprites.GetUnsafe(entity) assert.NotNil(sprite) @@ -102,7 +101,6 @@ func (s *SpriteSystem) Run() { tr.Dest.Width = sprite.Frame.Width tr.Dest.Height = sprite.Frame.Height tr.Tint = sprite.Tint - return true }) } func (s *SpriteSystem) Destroy() {} diff --git a/stdsystems/texture-position-smooth.go b/stdsystems/texture-position-smooth.go index 6e55e75f..461d0117 100644 --- a/stdsystems/texture-position-smooth.go +++ b/stdsystems/texture-position-smooth.go @@ -38,31 +38,28 @@ func (s *TexturePositionSmoothSystem) Init() { func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { //DEBUG Temporary, TODO: remove if rl.IsKeyPressed(rl.KeyI) { - s.TexturePositionSmooth.EachComponentParallel(s.Engine.Pool())(func(t *stdcomponents.TexturePositionSmooth, i worker.WorkerId) bool { + s.TexturePositionSmooth.ProcessComponents(func(t *stdcomponents.TexturePositionSmooth, workerId worker.WorkerId) { *t = stdcomponents.TexturePositionSmoothOff - return true }) } if rl.IsKeyPressed(rl.KeyO) { - s.TexturePositionSmooth.EachComponentParallel(s.Engine.Pool())(func(t *stdcomponents.TexturePositionSmooth, i worker.WorkerId) bool { + s.TexturePositionSmooth.ProcessComponents(func(t *stdcomponents.TexturePositionSmooth, workerId worker.WorkerId) { *t = stdcomponents.TexturePositionSmoothLerp - return true }) } if rl.IsKeyPressed(rl.KeyP) { - s.TexturePositionSmooth.EachComponentParallel(s.Engine.Pool())(func(t *stdcomponents.TexturePositionSmooth, i worker.WorkerId) bool { + s.TexturePositionSmooth.ProcessComponents(func(t *stdcomponents.TexturePositionSmooth, workerId worker.WorkerId) { *t = stdcomponents.TexturePositionSmoothExpDecay - return true }) } //END DEBUG - s.TexturePositionSmooth.EachEntityParallel(s.Engine.Pool())(func(entity ecs.Entity, i worker.WorkerId) bool { + s.TexturePositionSmooth.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { position := s.Position.GetUnsafe(entity) texture := s.RLTexture.GetUnsafe(entity) smooth := s.TexturePositionSmooth.GetUnsafe(entity) if texture == nil { - return true + return } assert.Nil(position, "position is nil") @@ -78,9 +75,8 @@ func (s *TexturePositionSmoothSystem) Run(dt time.Duration) { texture.Dest.X = xy.X texture.Dest.Y = xy.Y default: + panic("not implemented") } - - return true }) } diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 1637f599..fe3c64ec 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -37,7 +37,7 @@ func (s *VelocitySystem) Init() { func (s *VelocitySystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - s.Velocities.EachEntityParallel(s.Engine.Pool())(func(e ecs.Entity, workerId worker.WorkerId) bool { + s.Velocities.ProcessEntities(func(e ecs.Entity, workerId worker.WorkerId) { velocity := s.Velocities.GetUnsafe(e) assert.True(s.isVelocityValid(velocity)) @@ -46,7 +46,6 @@ func (s *VelocitySystem) Run(dt time.Duration) { position.XY.X += velocity.X * dtSec position.XY.Y += velocity.Y * dtSec - return true }) } From a0e7a7401fa4bf38d612cc4e392b8f7be285427a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 20 Apr 2025 14:04:48 +0300 Subject: [PATCH 149/196] fix pool nil pointer --- pkg/ecs/component-manager-shared.go | 4 ++-- pkg/ecs/component-manager.go | 4 ++-- pkg/ecs/paged-array.go | 17 ++++++++--------- pkg/ecs/world.go | 1 + 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 0e8c6c58..4dbe110e 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -253,13 +253,13 @@ func (c *SharedComponentManager[T]) Each() func(yield func(Entity, *T) bool) { func (c *SharedComponentManager[T]) ProcessComponents(handler func(*T, worker.WorkerId)) { c.assertBegin() defer c.assertEnd() - c.components.EachDataParallel(handler) + c.components.EachDataParallel(handler, c.pool) } func (c *SharedComponentManager[T]) EachEntityParallel(handler func(Entity, worker.WorkerId)) { c.assertBegin() defer c.assertEnd() - c.entities.ProcessDataValue(handler) + c.entities.ProcessDataValue(handler, c.pool) } func (c *SharedComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entity, *T, int) bool) { diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 29c1d973..3992749d 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -275,13 +275,13 @@ func (c *ComponentManager[T]) Each() func(yield func(entity Entity, component *T func (c *ComponentManager[T]) ProcessEntities(handler func(Entity, worker.WorkerId)) { c.assertBegin() defer c.assertEnd() - c.entities.ProcessDataValue(handler) + c.entities.ProcessDataValue(handler, c.pool) } func (c *ComponentManager[T]) ProcessComponents(handler func(*T, worker.WorkerId)) { c.assertBegin() defer c.assertEnd() - c.components.EachDataParallel(handler) + c.components.EachDataParallel(handler, c.pool) } func (c *ComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entity, *T, int) bool) { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index cc3fa037..da8c52ac 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -34,7 +34,6 @@ type PagedArray[T any] struct { // Cache edvpTasks []EachDataValueTask[T] edpTasks []EachDataTask[T] - pool *worker.Pool } type ArrayPage[T any] struct { @@ -276,27 +275,27 @@ func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { } } -func (a *PagedArray[T]) ProcessDataValue(handler func(T, worker.WorkerId)) { - assert.NotNil(a.pool) +func (a *PagedArray[T]) ProcessDataValue(handler func(T, worker.WorkerId), pool *worker.Pool) { + assert.NotNil(pool) for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i a.edvpTasks[j].page = &a.book[i] a.edvpTasks[j].f = handler - a.pool.ProcessGroupTask(&a.edvpTasks[j]) + pool.ProcessGroupTask(&a.edvpTasks[j]) } - a.pool.GroupWait() + pool.GroupWait() } -func (a *PagedArray[T]) EachDataParallel(handler func(*T, worker.WorkerId)) { - assert.NotNil(a.pool) +func (a *PagedArray[T]) EachDataParallel(handler func(*T, worker.WorkerId), pool *worker.Pool) { + assert.NotNil(pool) for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i a.edpTasks[j].page = &a.book[i] a.edpTasks[j].f = handler - a.pool.ProcessGroupTask(&a.edpTasks[j]) + pool.ProcessGroupTask(&a.edpTasks[j]) } - a.pool.GroupWait() + pool.GroupWait() } // ========================= diff --git a/pkg/ecs/world.go b/pkg/ecs/world.go index a3cacc5c..df95b401 100644 --- a/pkg/ecs/world.go +++ b/pkg/ecs/world.go @@ -72,6 +72,7 @@ func (w *World[C, S]) injectEntityManagerToComponents() { } entityManager.registerComponent(componentManager) componentManager.registerEntityManager(entityManager) + componentManager.registerWorkerPool(w.Engine.Pool()) } } From 233d4f32e2ac0d035ac4d0a48862ce634e2b965f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 20 Apr 2025 14:22:47 +0300 Subject: [PATCH 150/196] feat worker pool GroupAdd() method --- pkg/ecs/paged-array.go | 5 ++++- pkg/worker/pool.go | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index da8c52ac..4af9fdf3 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -276,7 +276,9 @@ func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { } func (a *PagedArray[T]) ProcessDataValue(handler func(T, worker.WorkerId), pool *worker.Pool) { + assert.NotNil(handler) assert.NotNil(pool) + pool.GroupAdd(a.currentPageIndex + 1) for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i a.edvpTasks[j].page = &a.book[i] @@ -287,8 +289,9 @@ func (a *PagedArray[T]) ProcessDataValue(handler func(T, worker.WorkerId), pool } func (a *PagedArray[T]) EachDataParallel(handler func(*T, worker.WorkerId), pool *worker.Pool) { + assert.NotNil(handler) assert.NotNil(pool) - + pool.GroupAdd(a.currentPageIndex + 1) for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i a.edpTasks[j].page = &a.book[i] diff --git a/pkg/worker/pool.go b/pkg/worker/pool.go index 1323fd0a..c65d9122 100644 --- a/pkg/worker/pool.go +++ b/pkg/worker/pool.go @@ -68,15 +68,18 @@ func (p *Pool) Stop() { p.wg.Wait() } -func (p *Pool) GroupWait() { - p.groupTaskWg.Wait() +func (p *Pool) GroupAdd(n int) { + p.groupTaskWg.Add(n) } func (p *Pool) ProcessGroupTask(tasks AnyTask) { - p.groupTaskWg.Add(1) p.taskChan <- tasks } +func (p *Pool) GroupWait() { + p.groupTaskWg.Wait() +} + func (p *Pool) NumWorkers() int { return len(p.workers) } From 21141e32ca5df7c08a056be475e32c18590bb9ea Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 20 Apr 2025 14:49:08 +0300 Subject: [PATCH 151/196] optimize memory allocation --- pkg/ecs/paged-array.go | 6 +++--- pkg/ecs/paged-map.go | 2 +- taskfile.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 4af9fdf3..01eebb48 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -13,9 +13,9 @@ import ( ) func NewPagedArray[T any]() (a PagedArray[T]) { - a.book = make([]ArrayPage[T], 2, initialBookSize) - a.edpTasks = make([]EachDataTask[T], 2, initialBookSize) - a.edvpTasks = make([]EachDataValueTask[T], 2, initialBookSize) + a.book = make([]ArrayPage[T], initialBookSize) + a.edpTasks = make([]EachDataTask[T], initialBookSize) + a.edvpTasks = make([]EachDataValueTask[T], initialBookSize) return a } diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 2e00894e..da92173b 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -9,7 +9,7 @@ package ecs const ( pageSizeShift = 11 pageSize = 1 << pageSizeShift - initialBookSize = 4 // Starting with a small initial book size + initialBookSize = 2 // Starting with a small initial book size ) type MapPage[K Entity | SharedComponentInstanceId, V any] map[K]V diff --git a/taskfile.yml b/taskfile.yml index d9bab054..e882d40b 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -60,7 +60,7 @@ tasks: env: CGO_ENABLED: 1 cmds: - - go build -o ./.dist/game-win64.exe -tags opengl43 ./examples/new-api + - go build -ldflags="-s -w" -o ./.dist/game-win64.exe -tags opengl43 ./examples/new-api build-mac: - task: build-darwin-amd64 From 4a31572ddcfee7ec159ce6879081d6f7fe8c79b9 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 20 Apr 2025 15:04:16 +0300 Subject: [PATCH 152/196] implement map.go - which is LESS PERFORMANT than paged-map.go --- pkg/ecs/map.go | 34 ++++++++++++++++++++++++++++++++++ pkg/ecs/paged-map.go | 2 -- 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 pkg/ecs/map.go diff --git a/pkg/ecs/map.go b/pkg/ecs/map.go new file mode 100644 index 00000000..abcd89be --- /dev/null +++ b/pkg/ecs/map.go @@ -0,0 +1,34 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package ecs + +type Map[K Entity | SharedComponentInstanceId, V any] struct { + book map[K]V +} + +func NewMap[K Entity | SharedComponentInstanceId, V any]() Map[K, V] { + return Map[K, V]{ + book: make(map[K]V, initialBookSize), + } +} + +func (m *Map[K, V]) Get(key K) (value V, ok bool) { + value, ok = m.book[key] + return value, ok +} + +func (m *Map[K, V]) Set(key K, value V) { + m.book[key] = value +} + +func (m *Map[K, V]) Delete(key K) { + delete(m.book, key) +} + +func (m *Map[K, V]) Len() int { + return len(m.book) +} diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index da92173b..d61085d5 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -12,8 +12,6 @@ const ( initialBookSize = 2 // Starting with a small initial book size ) -type MapPage[K Entity | SharedComponentInstanceId, V any] map[K]V - type PagedMap[K Entity | SharedComponentInstanceId, V any] struct { len int book []SlicePage[MapValue[V]] From 752cf050f793a82888acc643e2b1f06cc375387b Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 24 Apr 2025 01:33:17 +0300 Subject: [PATCH 153/196] implement flat multithreaded collision setup in collision-detection.go --- examples/new-api/instances/component-list.go | 2 + examples/new-api/systems/asterodd.go | 2 +- examples/new-api/systems/render-overlay.go | 49 ++++ pkg/ecs/accumulator.go | 92 +++++++ pkg/ecs/component-manager.go | 1 + pkg/ecs/paged-map.go | 4 +- stdcomponents/bvh-tree.go | 24 +- stdcomponents/collision-cell.go | 60 +++++ stdcomponents/collision-grid.go | 39 ++- stdcomponents/ids.go | 1 + stdsystems/bvh-tree.go | 3 +- stdsystems/collision-detection.go | 247 +++++++++++++------ stdsystems/sprite.go | 55 ++--- 13 files changed, 461 insertions(+), 118 deletions(-) create mode 100644 pkg/ecs/accumulator.go create mode 100644 stdcomponents/collision-cell.go diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index cfb64bb5..f75906ff 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -49,6 +49,7 @@ type ComponentList struct { FrameBuffer2D stdcomponents.FrameBuffer2DComponentManager CollisionGrid stdcomponents.CollisionGridComponentManager CollisionChunk stdcomponents.CollisionChunkComponentManager + CollisionCell stdcomponents.CollisionCellComponentManager Health components.HpComponentManager Controller components.ControllerComponentManager @@ -98,6 +99,7 @@ func NewComponentList() ComponentList { TexturePositionSmooth: stdcomponents.NewTexturePositionSmoothComponentManager(), CollisionGrid: stdcomponents.NewCollisionGridComponentManager(), CollisionChunk: stdcomponents.NewCollisionChunkComponentManager(), + CollisionCell: stdcomponents.NewCollisionCellComponentManager(), Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index 015a07b0..49a5191c 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -110,7 +110,7 @@ func (s *AssteroddSystem) Init() { entities.CreateWall(&wallManager, -1000, -1000, 0, 1000, 7000) entities.CreateWall(&wallManager, 5000, -1000, 0, 1000, 7000) - for range 30000 { + for range 30_000 { randPos := vectors.Vec2{ X: float32(rand.Intn(5000)) + float32(rand.Intn(1000))/10000, Y: float32(rand.Intn(5000)) + float32(rand.Intn(1000))/10000, diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index b17b2f65..6ef0dd78 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -37,6 +37,7 @@ type RenderOverlaySystem struct { Cameras *stdcomponents.CameraComponentManager FrameBuffer2D *stdcomponents.FrameBuffer2DComponentManager CollisionChunks *stdcomponents.CollisionChunkComponentManager + CollisionCells *stdcomponents.CollisionCellComponentManager Tints *stdcomponents.TintComponentManager BvhTrees *stdcomponents.BvhTreeComponentManager Positions *stdcomponents.PositionComponentManager @@ -101,6 +102,54 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { rl.BeginMode2D(camera.Camera2D) cameraRect := camera.Rect() + s.CollisionCells.EachEntity()(func(e ecs.Entity) bool { + cell := s.CollisionCells.GetUnsafe(e) + assert.NotNil(cell) + + if cell.Layer != stdcomponents.CollisionLayer(s.debugLvl) { + return true + } + + tint := s.Tints.GetUnsafe(e) + assert.NotNil(tint) + + position := s.Positions.GetUnsafe(e) + assert.NotNil(position) + + tree := s.BvhTrees.GetUnsafe(e) + assert.NotNil(tree) + + tree.AabbNodes.EachData()(func(a *stdcomponents.AABB) bool { + // Simple AABB culling + if s.intersects(cameraRect, a.Rect()) { + rl.DrawRectangleRec(rl.Rectangle{ + X: a.Min.X, + Y: a.Min.Y, + Width: a.Max.X - a.Min.X, + Height: a.Max.Y - a.Min.Y, + }, *tint) + } + return true + }) + + clr := color.RGBA{ + R: tint.R, + G: tint.G, + B: tint.B, + A: 255, + } + + // Simple AABB culling + if s.intersects(cameraRect, vectors.Rectangle{ + X: position.XY.X, + Y: position.XY.Y, + Width: cell.Size, + Height: cell.Size, + }) { + rl.DrawRectangleLines(int32(position.XY.X), int32(position.XY.Y), int32(cell.Size), int32(cell.Size), clr) + } + return true + }) s.CollisionChunks.EachEntity()(func(e ecs.Entity) bool { chunk := s.CollisionChunks.GetUnsafe(e) assert.NotNil(chunk) diff --git a/pkg/ecs/accumulator.go b/pkg/ecs/accumulator.go new file mode 100644 index 00000000..0fff717a --- /dev/null +++ b/pkg/ecs/accumulator.go @@ -0,0 +1,92 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "github.com/negrel/assert" + "gomp/pkg/worker" +) + +// NewAccumulator creates a new accumulator instance +func NewAccumulator[T any]( + initFn func() T, + resetFn func(T) T, + mergeFn func([]T) T, + processFn func(T), +) Accumulator[T] { + return Accumulator[T]{ + initFn: initFn, + resetFn: resetFn, + mergeFn: mergeFn, + processFn: processFn, + } +} + +// Accumulator handles multi-worker accumulation with flexible merge strategies +type Accumulator[T any] struct { + workerData []T // Per-worker storage + initFn func() T // Initializes worker-specific storage + resetFn func(T) T // Clears worker data while preserving allocations + mergeFn func([]T) T // Combines all worker data into final result + processFn func(T) // Processes worker data + isInitialized bool +} + +// Init initializes the accumulator +func (a *Accumulator[T]) Init(pool *worker.Pool) { + a.workerData = make([]T, pool.NumWorkers()) + for i := range a.workerData { + a.workerData[i] = a.initFn() + } + a.isInitialized = true +} + +// Update modifies worker-specific data in a thread-safe manner +func (a *Accumulator[T]) Update(workerID worker.WorkerId, update func(acc T) T) { + assert.True(a.isInitialized) + a.workerData[workerID] = update(a.workerData[workerID]) +} + +// Merge combines all worker data into final result +func (a *Accumulator[T]) Merge() T { + assert.True(a.isInitialized) + return a.mergeFn(a.workerData) +} + +// Process applies finalization function to all collected data +func (a *Accumulator[T]) Process() { + assert.True(a.isInitialized) + for i := range a.workerData { + a.processFn(a.workerData[i]) + } +} + +// Reset prepares the accumulator for new data collection +func (a *Accumulator[T]) Reset() { + assert.True(a.isInitialized) + for i := range a.workerData { + a.workerData[i] = a.resetFn(a.workerData[i]) + } +} + +// Destroy releases all resources associated with the accumulator +func (a *Accumulator[T]) Destroy() { + a.workerData = nil + a.initFn = nil + a.resetFn = nil + a.mergeFn = nil + a.processFn = nil + a.isInitialized = false +} diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 3992749d..1630e1e5 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -9,6 +9,7 @@ Donations during this file development: <- Hininn Donated 2 000 RUB <- Сосисочник Паша Donated 77 RUB +<- mitwelve Donated 1 000 RUB Thank you for your support! */ diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index d61085d5..60f51f09 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -7,9 +7,9 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs const ( - pageSizeShift = 11 + pageSizeShift = 10 pageSize = 1 << pageSizeShift - initialBookSize = 2 // Starting with a small initial book size + initialBookSize = 1 // Starting with a small initial book size ) type PagedMap[K Entity | SharedComponentInstanceId, V any] struct { diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index eb34f3f6..53477efa 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -40,12 +40,21 @@ type BvhComponent struct { } type BvhTree struct { - Nodes ecs.Slice[BvhNode] - AabbNodes ecs.Slice[AABB] - Leaves ecs.Slice[BvhLeaf] - AabbLeaves ecs.Slice[AABB] - Codes ecs.Slice[uint64] - Components ecs.Slice[BvhComponent] + Nodes ecs.PagedArray[BvhNode] + AabbNodes ecs.PagedArray[AABB] + Leaves ecs.PagedArray[BvhLeaf] + AabbLeaves ecs.PagedArray[AABB] + Codes ecs.PagedArray[uint64] + Components ecs.PagedArray[BvhComponent] +} + +func (t *BvhTree) Init() { + t.Nodes = ecs.NewPagedArray[BvhNode]() + t.AabbNodes = ecs.NewPagedArray[AABB]() + t.Leaves = ecs.NewPagedArray[BvhLeaf]() + t.AabbLeaves = ecs.NewPagedArray[AABB]() + t.Codes = ecs.NewPagedArray[uint64]() + t.Components = ecs.NewPagedArray[BvhComponent]() } func (t *BvhTree) AddComponent(entity ecs.Entity, aabb AABB) { @@ -150,7 +159,8 @@ func (t *BvhTree) Build() { t.AabbLeaves.Reset() t.Codes.Reset() - var sorted = t.Components.Raw() + var sorted []BvhComponent + sorted = t.Components.Raw(sorted) slices.SortFunc(sorted, func(a, b BvhComponent) int { return int(a.Code - b.Code) diff --git a/stdcomponents/collision-cell.go b/stdcomponents/collision-cell.go new file mode 100644 index 00000000..afd0bf71 --- /dev/null +++ b/stdcomponents/collision-cell.go @@ -0,0 +1,60 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "github.com/negrel/assert" + "gomp/pkg/ecs" + "gomp/pkg/worker" +) + +type CollisionCell struct { + Members ecs.PagedArray[ecs.Entity] + MemberLookup ecs.PagedMap[ecs.Entity, int] + + InputAccumulator []ecs.PagedArray[ecs.Entity] + Size float32 + Layer CollisionLayer +} + +func (c *CollisionCell) Init(size float32, layer CollisionLayer, pool *worker.Pool) { + c.Members = ecs.NewPagedArray[ecs.Entity]() + c.MemberLookup = ecs.NewPagedMap[ecs.Entity, int]() + c.InputAccumulator = make([]ecs.PagedArray[ecs.Entity], pool.NumWorkers()) + for i := 0; i < pool.NumWorkers(); i++ { + c.InputAccumulator[i] = ecs.NewPagedArray[ecs.Entity]() + } + c.Size = size + c.Layer = layer +} + +func (c *CollisionCell) AddMember(entity ecs.Entity) { + c.Members.Append(entity) + c.MemberLookup.Set(entity, c.Members.Len()-1) +} + +func (c *CollisionCell) RemoveMember(entity ecs.Entity) { + index, ok := c.MemberLookup.Get(entity) + assert.True(ok) + c.Members.Swap(index, c.Members.Len()-1) + c.Members.SoftReduce() + c.MemberLookup.Delete(entity) +} + +type CollisionCellComponentManager = ecs.ComponentManager[CollisionCell] + +func NewCollisionCellComponentManager() CollisionCellComponentManager { + return ecs.NewComponentManager[CollisionCell](CollisionCellComponentId) +} diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go index 3a3c5c99..37b5adf8 100644 --- a/stdcomponents/collision-grid.go +++ b/stdcomponents/collision-grid.go @@ -16,32 +16,53 @@ package stdcomponents import ( "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/vectors" + "math" ) type CollisionGrid struct { Layer CollisionLayer // Layer of the grid Entities ecs.PagedArray[ecs.Entity] // List of Entities in the grid ChunkLookup map[SpatialIndex]ecs.Entity // Pointer to cell + ChunkSize float32 + MinBounds vectors.Vec2 - ChunkSize float32 - MinBounds vectors.Vec2 + // NEW API + Cells ecs.PagedArray[ecs.Entity] // List of Cells in the grid + CellLookup map[SpatialIndex]int // Index to cell in Cells + CellSizeAccumulator []float32 // Accumulator for cell size + CellAccumulator []map[SpatialIndex]struct{} // Accumulator for Cells + CellSize float32 // Size of a cell } -func (t *CollisionGrid) RegisterEntity(entity ecs.Entity, aabb *AABB) { - t.Entities.Append(entity) +func (g *CollisionGrid) Init(collisionLayer CollisionLayer, pool *worker.Pool) { + g.Layer = collisionLayer + g.CellSize = math.MaxFloat32 + g.Cells = ecs.NewPagedArray[ecs.Entity]() + g.CellLookup = make(map[SpatialIndex]int) + g.CellSizeAccumulator = make([]float32, pool.NumWorkers()) + g.CellAccumulator = make([]map[SpatialIndex]struct{}, pool.NumWorkers()) + for i := 0; i < pool.NumWorkers(); i++ { + g.CellSizeAccumulator[i] = math.MaxFloat32 + g.CellAccumulator[i] = make(map[SpatialIndex]struct{}) + } +} + +func (g *CollisionGrid) RegisterEntity(entity ecs.Entity, aabb *AABB) { + g.Entities.Append(entity) l := aabb.Max.Sub(aabb.Min) - if l.LengthSquared() < t.MinBounds.LengthSquared() { - t.MinBounds = l + if l.LengthSquared() < g.MinBounds.LengthSquared() { + g.MinBounds = l } } -func (t *CollisionGrid) GetSpatialIndex(position vectors.Vec2) SpatialIndex { +func (g *CollisionGrid) GetSpatialIndex(position vectors.Vec2) SpatialIndex { return SpatialIndex{ - X: int(position.X / t.ChunkSize), - Y: int(position.Y / t.ChunkSize), + X: int(position.X / g.CellSize), + Y: int(position.Y / g.CellSize), } } diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index eeddbe64..302d5d23 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -41,6 +41,7 @@ const ( RigidBodyComponentId BvhTreeComponentId CollisionGridComponentId + CollisionCellComponentId CollisionChunkComponentId FrameBuffer2DComponentId CameraComponentId diff --git a/stdsystems/bvh-tree.go b/stdsystems/bvh-tree.go index 1b1d2764..baf6a22a 100644 --- a/stdsystems/bvh-tree.go +++ b/stdsystems/bvh-tree.go @@ -44,7 +44,8 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { t.AabbLeaves.Reset() t.Codes.Reset() - var sorted = t.Components.Raw() + var sorted []stdcomponents.BvhComponent + sorted = t.Components.Raw(sorted) slices.SortFunc(sorted, func(a, b stdcomponents.BvhComponent) int { return cmp.Compare(a.Code, b.Code) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 267aa8ad..4a37cde6 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -49,6 +49,7 @@ type CollisionDetectionSystem struct { BvhTreeComponentManager *stdcomponents.BvhTreeComponentManager CollisionGridComponentManager *stdcomponents.CollisionGridComponentManager CollisionChunkComponentManager *stdcomponents.CollisionChunkComponentManager + CollisionCellComponentManager *stdcomponents.CollisionCellComponentManager gridLookup map[stdcomponents.CollisionLayer]ecs.Entity numWorkers int @@ -61,7 +62,6 @@ func (s *CollisionDetectionSystem) Init() { } func (s *CollisionDetectionSystem) Run(dt time.Duration) { s.setup() - } func (s *CollisionDetectionSystem) Destroy() {} func (s *CollisionDetectionSystem) setup() { @@ -93,29 +93,87 @@ func (s *CollisionDetectionSystem) setup() { if collisionLayerAccumulator&(1< Date: Thu, 24 Apr 2025 13:22:04 +0300 Subject: [PATCH 154/196] rename collision-detection.go to collision-setup.go --- examples/new-api/game.go | 8 +-- examples/new-api/instances/system-list.go | 4 +- ...lision-detection.go => collision-setup.go} | 72 +++++++++---------- 3 files changed, 40 insertions(+), 44 deletions(-) rename stdsystems/{collision-detection.go => collision-setup.go} (88%) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 0e8f1021..987c9a40 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -66,7 +66,7 @@ func (g *Game) Init(engine *core.Engine) { systems.ColliderSystem.Init() systems.Velocity.Init() - systems.CollisionDetection.Init() + systems.CollisionSetup.Init() systems.CollisionDetectionBVH.Init() systems.CollisionResolution.Init() systems.AnimationSpriteMatrix.Init() @@ -104,8 +104,8 @@ func (g *Game) FixedUpdate(dt time.Duration) { systems.Velocity.Run(dt) systems.ColliderSystem.Run(dt) - //systems.CollisionDetection.Run(dt) - systems.CollisionDetectionBVH.Run(dt) + systems.CollisionSetup.Run(dt) + //systems.CollisionDetectionBVH.Run(dt) systems.CollisionResolution.Run(dt) scene.FixedUpdate(dt) @@ -145,7 +145,7 @@ func (g *Game) Destroy() { scene.Destroy() systems.Velocity.Destroy() - systems.CollisionDetection.Destroy() + systems.CollisionSetup.Destroy() systems.CollisionDetectionBVH.Destroy() systems.CollisionResolution.Destroy() systems.AnimationSpriteMatrix.Destroy() diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index d807d5bd..8bda396b 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -35,7 +35,7 @@ func NewSystemList() SystemList { SpriteMatrix: stdsystems.NewSpriteMatrixSystem(), AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{&assets.Textures, &assets.Audio}), YSort: stdsystems.NewYSortSystem(), - CollisionDetection: stdsystems.NewCollisionDetectionSystem(), + CollisionSetup: stdsystems.NewCollisionSetupSystem(), CollisionDetectionGrid: stdsystems.NewCollisionDetectionGridSystem(), CollisionDetectionBVH: stdsystems.NewCollisionDetectionBVHSystem(), ColliderSystem: stdsystems.NewColliderSystem(), @@ -77,10 +77,10 @@ type SystemList struct { SpriteMatrix stdsystems.SpriteMatrixSystem AssetLib stdsystems.AssetLibSystem YSort stdsystems.YSortSystem - CollisionDetection stdsystems.CollisionDetectionSystem CollisionDetectionGrid stdsystems.CollisionDetectionGridSystem CollisionDetectionBVH stdsystems.CollisionDetectionBVHSystem ColliderSystem stdsystems.ColliderSystem + CollisionSetup stdsystems.CollisionSetupSystem CollisionResolution stdsystems.CollisionResolutionSystem TexturePositionSmooth stdsystems.TexturePositionSmoothSystem RenderCameras stdsystems.Render2DCamerasSystem diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-setup.go similarity index 88% rename from stdsystems/collision-detection.go rename to stdsystems/collision-setup.go index 4a37cde6..c08d7983 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-setup.go @@ -24,15 +24,14 @@ import ( "image/color" "math" "math/rand" - "runtime" "time" ) -func NewCollisionDetectionSystem() CollisionDetectionSystem { - return CollisionDetectionSystem{} +func NewCollisionSetupSystem() CollisionSetupSystem { + return CollisionSetupSystem{} } -type CollisionDetectionSystem struct { +type CollisionSetupSystem struct { EntityManager *ecs.EntityManager Positions *stdcomponents.PositionComponentManager Rotations *stdcomponents.RotationComponentManager @@ -52,19 +51,13 @@ type CollisionDetectionSystem struct { CollisionCellComponentManager *stdcomponents.CollisionCellComponentManager gridLookup map[stdcomponents.CollisionLayer]ecs.Entity - numWorkers int Engine *core.Engine } -func (s *CollisionDetectionSystem) Init() { +func (s *CollisionSetupSystem) Init() { s.gridLookup = make(map[stdcomponents.CollisionLayer]ecs.Entity) - s.numWorkers = runtime.NumCPU() - 2 } -func (s *CollisionDetectionSystem) Run(dt time.Duration) { - s.setup() -} -func (s *CollisionDetectionSystem) Destroy() {} -func (s *CollisionDetectionSystem) setup() { +func (s *CollisionSetupSystem) Run(dt time.Duration) { // Reset grids s.CollisionGridComponentManager.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { grid := s.CollisionGridComponentManager.GetUnsafe(entity) @@ -274,37 +267,40 @@ func (s *CollisionDetectionSystem) setup() { cell.InputAccumulator[id].Append(entity) }) - // Build grid cells - s.CollisionCellComponentManager.ProcessComponents(func(cell *stdcomponents.CollisionCell, id worker.WorkerId) { + //Build grid cells + //s.CollisionCellComponentManager.ProcessComponents(func(cell *stdcomponents.CollisionCell, id worker.WorkerId) { + // for i := range cell.InputAccumulator { + // for e := range cell.InputAccumulator[i].EachDataValue() { + // cell.AddMember(e) + // } + // cell.InputAccumulator[i].Reset() + // } + //}) + + //Build bvh trees + s.CollisionCellComponentManager.ProcessEntities(func(entity ecs.Entity, id worker.WorkerId) { + cell := s.CollisionCellComponentManager.GetUnsafe(entity) + assert.NotNil(cell) + + bvhTree := s.BvhTreeComponentManager.GetUnsafe(entity) + assert.NotNil(bvhTree) + for i := range cell.InputAccumulator { for e := range cell.InputAccumulator[i].EachDataValue() { - cell.AddMember(e) + aabb := s.AABB.GetUnsafe(e) + assert.NotNil(aabb) + bvhTree.AddComponent(e, *aabb) } cell.InputAccumulator[i].Reset() } }) + s.CollisionCellComponentManager.ProcessEntities(func(entity ecs.Entity, id worker.WorkerId) { + bvhTree := s.BvhTreeComponentManager.GetUnsafe(entity) + assert.NotNil(bvhTree) - // Build bvh trees - //s.CollisionCellComponentManager.ProcessEntities(func(entity ecs.Entity, id worker.WorkerId) { - // cell := s.CollisionCellComponentManager.GetUnsafe(entity) - // assert.NotNil(cell) - // - // bvhTree := s.BvhTreeComponentManager.GetUnsafe(entity) - // assert.NotNil(bvhTree) - // - // for i := range cell.InputAccumulator { - // for e := range cell.InputAccumulator[i].EachDataValue() { - // aabb := s.AABB.GetUnsafe(e) - // assert.NotNil(aabb) - // bvhTree.AddComponent(e, *aabb) - // } - // cell.InputAccumulator[i].Reset() - // } - //}) - //s.CollisionCellComponentManager.ProcessEntities(func(entity ecs.Entity, id worker.WorkerId) { - // bvhTree := s.BvhTreeComponentManager.GetUnsafe(entity) - // assert.NotNil(bvhTree) - // - // bvhTree.Build() - //}) + bvhTree.Build() + }) +} +func (s *CollisionSetupSystem) Destroy() { + s.gridLookup = nil } From 7edccb62b287ddbdf181e581ca2bbb267e9f5148 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 24 Apr 2025 18:29:05 +0300 Subject: [PATCH 155/196] feat collision-detection.go --- examples/new-api/game.go | 5 +- examples/new-api/instances/system-list.go | 4 +- stdcomponents/bvh-tree.go | 11 +- stdcomponents/collision-grid.go | 24 +- stdsystems/collision-detection.go | 262 ++++++++++++++++++++++ stdsystems/collision-setup.go | 6 +- 6 files changed, 302 insertions(+), 10 deletions(-) create mode 100644 stdsystems/collision-detection.go diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 987c9a40..4340658d 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -67,7 +67,8 @@ func (g *Game) Init(engine *core.Engine) { systems.ColliderSystem.Init() systems.Velocity.Init() systems.CollisionSetup.Init() - systems.CollisionDetectionBVH.Init() + systems.CollisionDetection.Init() + //systems.CollisionDetectionBVH.Init() systems.CollisionResolution.Init() systems.AnimationSpriteMatrix.Init() systems.AnimationPlayer.Init() @@ -105,6 +106,7 @@ func (g *Game) FixedUpdate(dt time.Duration) { systems.Velocity.Run(dt) systems.ColliderSystem.Run(dt) systems.CollisionSetup.Run(dt) + systems.CollisionDetection.Run(dt) //systems.CollisionDetectionBVH.Run(dt) systems.CollisionResolution.Run(dt) @@ -146,6 +148,7 @@ func (g *Game) Destroy() { systems.Velocity.Destroy() systems.CollisionSetup.Destroy() + systems.CollisionDetection.Destroy() systems.CollisionDetectionBVH.Destroy() systems.CollisionResolution.Destroy() systems.AnimationSpriteMatrix.Destroy() diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index 8bda396b..07a827cc 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -35,10 +35,11 @@ func NewSystemList() SystemList { SpriteMatrix: stdsystems.NewSpriteMatrixSystem(), AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{&assets.Textures, &assets.Audio}), YSort: stdsystems.NewYSortSystem(), - CollisionSetup: stdsystems.NewCollisionSetupSystem(), CollisionDetectionGrid: stdsystems.NewCollisionDetectionGridSystem(), CollisionDetectionBVH: stdsystems.NewCollisionDetectionBVHSystem(), ColliderSystem: stdsystems.NewColliderSystem(), + CollisionSetup: stdsystems.NewCollisionSetupSystem(), + CollisionDetection: stdsystems.NewCollisionDetectionSystem(), CollisionResolution: stdsystems.NewCollisionResolutionSystem(), TexturePositionSmooth: stdsystems.NewTexturePositionSmoothSystem(), RenderCameras: stdsystems.NewRender2DCamerasSystem(), @@ -81,6 +82,7 @@ type SystemList struct { CollisionDetectionBVH stdsystems.CollisionDetectionBVHSystem ColliderSystem stdsystems.ColliderSystem CollisionSetup stdsystems.CollisionSetupSystem + CollisionDetection stdsystems.CollisionDetectionSystem CollisionResolution stdsystems.CollisionResolutionSystem TexturePositionSmooth stdsystems.TexturePositionSmoothSystem RenderCameras stdsystems.Render2DCamerasSystem diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index 53477efa..85143f55 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -66,7 +66,7 @@ func (t *BvhTree) AddComponent(entity ecs.Entity, aabb AABB) { }) } -func (t *BvhTree) Query(aabb *AABB, result []ecs.Entity) []ecs.Entity { +func (t *BvhTree) Query(aabb AABB, result []ecs.Entity) []ecs.Entity { if t.Nodes.Len() == 0 { // Handle empty tree return result } @@ -79,11 +79,10 @@ func (t *BvhTree) Query(aabb *AABB, result []ecs.Entity) []ecs.Entity { for stackLen > 0 { stackLen-- nodeIndex := int(stack[stackLen]) - a := t.AabbNodes.Get(nodeIndex) - b := aabb + a := t.AabbNodes.GetValue(nodeIndex) // Early exit if no AABB overlap - if !t.aabbOverlap(a, b) { + if !t.aabbOverlap(a, aabb) { continue } @@ -91,7 +90,7 @@ func (t *BvhTree) Query(aabb *AABB, result []ecs.Entity) []ecs.Entity { if node.ChildIndex <= 0 { // Is a leaf index := -int(node.ChildIndex) - leafAabb := t.AabbLeaves.Get(index) + leafAabb := t.AabbLeaves.GetValue(index) if t.aabbOverlap(leafAabb, aabb) { result = append(result, t.Leaves.Get(index).Id) } @@ -108,7 +107,7 @@ func (t *BvhTree) Query(aabb *AABB, result []ecs.Entity) []ecs.Entity { } // go:inline aabbOverlap checks if two AABB intersect -func (t *BvhTree) aabbOverlap(a, b *AABB) bool { +func (t *BvhTree) aabbOverlap(a, b AABB) bool { return a.Max.X >= b.Min.X && a.Min.X <= b.Max.X && a.Max.Y >= b.Min.Y && a.Min.Y <= b.Max.Y } diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go index 37b5adf8..1fe921ee 100644 --- a/stdcomponents/collision-grid.go +++ b/stdcomponents/collision-grid.go @@ -22,13 +22,13 @@ import ( ) type CollisionGrid struct { - Layer CollisionLayer // Layer of the grid Entities ecs.PagedArray[ecs.Entity] // List of Entities in the grid ChunkLookup map[SpatialIndex]ecs.Entity // Pointer to cell ChunkSize float32 MinBounds vectors.Vec2 // NEW API + Layer CollisionLayer // Layer of the grid Cells ecs.PagedArray[ecs.Entity] // List of Cells in the grid CellLookup map[SpatialIndex]int // Index to cell in Cells CellSizeAccumulator []float32 // Accumulator for cell size @@ -49,6 +49,28 @@ func (g *CollisionGrid) Init(collisionLayer CollisionLayer, pool *worker.Pool) { } } +// Query returns the EntityIds of Cells that intersect the AABB +func (g *CollisionGrid) Query(bb AABB, result []ecs.Entity) []ecs.Entity { + // get spatial index of aabb + minSpatialIndex := g.GetSpatialIndex(bb.Min) + maxSpatialIndex := g.GetSpatialIndex(bb.Max) + // make a list of all spatial indexes that intersect the aabb + // get cells that intersect the aabb by spatial indexes + for i := minSpatialIndex.X; i <= maxSpatialIndex.X; i++ { + for j := minSpatialIndex.Y; j <= maxSpatialIndex.Y; j++ { + spatialIndex := SpatialIndex{X: i, Y: j} + cellIndex, exists := g.CellLookup[spatialIndex] + if !exists { + continue + + } + cell := g.Cells.GetValue(cellIndex) + result = append(result, cell) + } + } + return result +} + func (g *CollisionGrid) RegisterEntity(entity ecs.Entity, aabb *AABB) { g.Entities.Append(entity) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go new file mode 100644 index 00000000..06acc5cf --- /dev/null +++ b/stdsystems/collision-detection.go @@ -0,0 +1,262 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "github.com/negrel/assert" + gjk "gomp/pkg/collision" + "gomp/pkg/core" + "gomp/pkg/ecs" + "gomp/pkg/worker" + "gomp/stdcomponents" + "gomp/vectors" + "time" +) + +func NewCollisionDetectionSystem() CollisionDetectionSystem { + return CollisionDetectionSystem{} +} + +type CollisionDetectionSystem struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + PolygonColliders *stdcomponents.PolygonColliderComponentManager + Collisions *stdcomponents.CollisionComponentManager + SpatialIndex *stdcomponents.SpatialIndexComponentManager + AABB *stdcomponents.AABBComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + BvhTreeComponentManager *stdcomponents.BvhTreeComponentManager + CollisionGridComponentManager *stdcomponents.CollisionGridComponentManager + CollisionChunkComponentManager *stdcomponents.CollisionChunkComponentManager + CollisionCellComponentManager *stdcomponents.CollisionCellComponentManager + Engine *core.Engine + + collisionEventAcc []ecs.PagedArray[CollisionEvent] + activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities + currentCollisions map[CollisionPair]struct{} + gridLookup map[stdcomponents.CollisionLayer]*stdcomponents.CollisionGrid +} + +func (s *CollisionDetectionSystem) Init() { + s.gridLookup = make(map[stdcomponents.CollisionLayer]*stdcomponents.CollisionGrid) + s.collisionEventAcc = make([]ecs.PagedArray[CollisionEvent], s.Engine.Pool().NumWorkers()) + for i := 0; i < s.Engine.Pool().NumWorkers(); i++ { + s.collisionEventAcc[i] = ecs.NewPagedArray[CollisionEvent]() + } + s.activeCollisions = make(map[CollisionPair]ecs.Entity) +} +func (s *CollisionDetectionSystem) Run(dt time.Duration) { + s.CollisionGridComponentManager.EachComponent()(func(grid *stdcomponents.CollisionGrid) bool { + s.gridLookup[grid.Layer] = grid + return true + }) + + s.currentCollisions = make(map[CollisionPair]struct{}) + + if s.GenericCollider.Len() == 0 { + return + } + + s.GenericCollider.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { + potentialEntities := s.broadPhase(entity, make([]ecs.Entity, 0, 64)) + if len(potentialEntities) == 0 { + return + } + + s.narrowPhase(entity, potentialEntities, workerId) + }) + + s.registerCollisionEvents() + s.processExitStates() +} +func (s *CollisionDetectionSystem) Destroy() { + s.collisionEventAcc = nil + s.activeCollisions = nil + s.currentCollisions = nil + s.gridLookup = nil +} + +func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, result []ecs.Entity) []ecs.Entity { + colliderA := s.GenericCollider.GetUnsafe(entityA) + if colliderA.AllowSleep { + if s.ColliderSleepStateComponentManager.Has(entityA) { + return result + } + } + + aabbPtr := s.AABB.GetUnsafe(entityA) + assert.NotNil(aabbPtr) + aabb := *aabbPtr + + cells := make([]ecs.Entity, 0, 64) + + // Iterate through all trees + for index := range s.gridLookup { + grid := s.gridLookup[index] + layer := grid.Layer + + // Check if mask includes this layer + if !colliderA.Mask.HasLayer(layer) { + continue + } + + // Traverse this BVH tree for potential collisions + cells = grid.Query(aabb, cells) + } + + for _, cellEntityId := range cells { + tree := s.BvhTreeComponentManager.GetUnsafe(cellEntityId) + assert.NotNil(tree) + + result = tree.Query(aabb, result) + } + + return result +} + +func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEntities []ecs.Entity, workerId worker.WorkerId) { + for _, entityB := range potentialEntities { + if entityA == entityB { + continue + } + + colliderA := s.GenericCollider.GetUnsafe(entityA) + colliderB := s.GenericCollider.GetUnsafe(entityB) + posA := s.Positions.GetUnsafe(entityA) + posB := s.Positions.GetUnsafe(entityB) + scaleA := s.Scales.GetUnsafe(entityA) + scaleB := s.Scales.GetUnsafe(entityB) + rotA := s.Rotations.GetUnsafe(entityA) + rotB := s.Rotations.GetUnsafe(entityB) + transformA := stdcomponents.Transform2d{ + Position: posA.XY, + Rotation: rotA.Angle, + Scale: scaleA.XY, + } + transformB := stdcomponents.Transform2d{ + Position: posB.XY, + Rotation: rotB.Angle, + Scale: scaleB.XY, + } + + circleA := s.CircleColliders.GetUnsafe(entityA) + circleB := s.CircleColliders.GetUnsafe(entityB) + if circleA != nil && circleB != nil { + radiusA := circleA.Radius * scaleA.XY.X + radiusB := circleB.Radius * scaleB.XY.X + if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { + s.collisionEventAcc[workerId].Append(CollisionEvent{ + entityA: entityA, + entityB: entityB, + position: transformA.Position, + normal: transformB.Position.Sub(transformA.Position).Normalize(), + depth: radiusA + radiusB - transformB.Position.Distance(transformA.Position), + }) + } + continue + } + + // GJK strategy + colA := s.getGjkCollider(colliderA, entityA) + colB := s.getGjkCollider(colliderB, entityB) + // First detect collision using GJK + test := gjk.New() + if !test.CheckCollision(colA, colB, transformA, transformB) { + continue + } + + // If collision detected, get penetration details using EPA + normal, depth := test.EPA(colA, colB, transformA, transformB) + position := posA.XY.Add(posB.XY.Sub(posA.XY)) + s.collisionEventAcc[workerId].Append(CollisionEvent{ + entityA: entityA, + entityB: entityB, + position: position, + normal: normal, + depth: depth, + }) + } +} +func (s *CollisionDetectionSystem) registerCollisionEvents() { + for i := range s.collisionEventAcc { + events := &s.collisionEventAcc[i] + events.EachData()(func(event *CollisionEvent) bool { + pair := CollisionPair{event.entityA, event.entityB} + s.currentCollisions[pair] = struct{}{} + displacement := event.normal.Scale(event.depth) + pos := event.position.Add(displacement) + + if _, exists := s.activeCollisions[pair]; !exists { + proxy := s.EntityManager.Create() + s.Collisions.Create(proxy, stdcomponents.Collision{ + E1: pair.E1, + E2: pair.E2, + State: stdcomponents.CollisionStateEnter, + Normal: event.normal, + Depth: event.depth, + }) + + s.Positions.Create(proxy, stdcomponents.Position{ + XY: vectors.Vec2{ + X: pos.X, Y: pos.Y, + }}) + s.activeCollisions[pair] = proxy + } else { + proxy := s.activeCollisions[pair] + collision := s.Collisions.GetUnsafe(proxy) + position := s.Positions.GetUnsafe(proxy) + collision.State = stdcomponents.CollisionStateStay + collision.Depth = event.depth + collision.Normal = event.normal + position.XY.X = pos.X + position.XY.Y = pos.Y + } + return true + }) + events.Reset() + } +} + +func (s *CollisionDetectionSystem) processExitStates() { + for pair, proxy := range s.activeCollisions { + if _, exists := s.currentCollisions[pair]; !exists { + collision := s.Collisions.GetUnsafe(proxy) + if collision.State == stdcomponents.CollisionStateExit { + delete(s.activeCollisions, pair) + s.EntityManager.Delete(proxy) + } else { + collision.State = stdcomponents.CollisionStateExit + } + } + } +} +func (s *CollisionDetectionSystem) getGjkCollider(collider *stdcomponents.GenericCollider, entity ecs.Entity) gjk.AnyCollider { + switch collider.Shape { + case stdcomponents.BoxColliderShape: + return s.BoxColliders.GetUnsafe(entity) + case stdcomponents.CircleColliderShape: + return s.CircleColliders.GetUnsafe(entity) + case stdcomponents.PolygonColliderShape: + return s.PolygonColliders.GetUnsafe(entity) + default: + panic("unsupported collider shape") + } + return nil +} diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index c08d7983..8c62bd42 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -27,6 +27,10 @@ import ( "time" ) +const ( + collidersPerCell = 64 +) + func NewCollisionSetupSystem() CollisionSetupSystem { return CollisionSetupSystem{} } @@ -139,7 +143,7 @@ func (s *CollisionSetupSystem) Run(dt time.Duration) { for i := range grid.CellSizeAccumulator { grid.CellSize = min(grid.CellSize, grid.CellSizeAccumulator[i]) } - grid.CellSize *= 32 + grid.CellSize *= collidersPerCell return true }) From 3804dd03b68dcebd2d1e796caf233457663157cc Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 24 Apr 2025 21:54:13 +0300 Subject: [PATCH 156/196] fix allocation issues --- examples/new-api/entities/wall.go | 5 +++ examples/new-api/scenes/assterodd-scene.go | 2 +- examples/new-api/systems/asterodd.go | 6 +++- examples/new-api/systems/collision-handler.go | 10 +++--- examples/new-api/systems/damping.go | 6 ++-- pkg/ecs/component-manager.go | 4 +-- pkg/ecs/paged-array.go | 13 ++++++-- stdcomponents/bvh-tree.go | 16 ++++++--- stdcomponents/collision-cell.go | 33 +++++++++---------- stdsystems/collision-detection.go | 6 +++- stdsystems/collision-reslution.go | 7 ++-- stdsystems/collision-setup.go | 10 +++--- 12 files changed, 72 insertions(+), 46 deletions(-) diff --git a/examples/new-api/entities/wall.go b/examples/new-api/entities/wall.go index 400143f4..b5532ed9 100644 --- a/examples/new-api/entities/wall.go +++ b/examples/new-api/entities/wall.go @@ -35,6 +35,7 @@ type CreateWallManagers struct { Sprites *stdcomponents.SpriteComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager Renderables *stdcomponents.RenderableComponentManager + Velocities *stdcomponents.VelocityComponentManager WallTags *components.WallTagComponentManager } @@ -74,6 +75,10 @@ func CreateWall( IsStatic: true, Mass: math.MaxFloat32, }) + props.Velocities.Create(entity, stdcomponents.Velocity{ + X: 0, + Y: 0, + }) props.Sprites.Create(entity, stdcomponents.Sprite{ Texture: assets.Textures.Get("wall.png"), Frame: rl.Rectangle{ diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index e379a60c..aeb756a6 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -93,7 +93,7 @@ func (s *AssteroddScene) Update(dt time.Duration) gomp.SceneId { func (s *AssteroddScene) FixedUpdate(dt time.Duration) { s.World.Systems.SpaceshipIntents.Run(dt) s.World.Systems.DampingSystem.Run(dt) - s.World.Systems.SpaceSpawner.Run(dt) + //s.World.Systems.SpaceSpawner.Run(dt) s.World.Systems.CollisionHandler.Run(dt) s.World.Systems.Hp.Run(dt) } diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index 49a5191c..d45f98f2 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -104,6 +104,7 @@ func (s *AssteroddSystem) Init() { WallTags: s.WallTags, RigidBodies: s.RigidBodies, Renderables: s.Renderables, + Velocities: s.Velocities, } entities.CreateWall(&wallManager, 0, -1000, 0, 5000, 1000) entities.CreateWall(&wallManager, 0, 5000, 0, 5000, 1000) @@ -116,6 +117,9 @@ func (s *AssteroddSystem) Init() { Y: float32(rand.Intn(5000)) + float32(rand.Intn(1000))/10000, } + randVelX := float32(rand.Intn(200)) - 100 + float32(rand.Intn(1000))/10000 + randVelY := float32(rand.Intn(200)) - 100 + float32(rand.Intn(1000))/10000 + entities.CreateBullet(entities.CreateBulletManagers{ EntityManager: s.EntityManager, Positions: s.Positions, @@ -128,7 +132,7 @@ func (s *AssteroddSystem) Init() { BulletTags: s.BulletTags, Hps: s.Hps, Renderables: s.Renderables, - }, randPos.X, randPos.Y, 0, 0, 0) + }, randPos.X, randPos.Y, 0, randVelX, randVelY) // bug case //entities.CreateBullet(entities.CreateBulletManagers{ diff --git a/examples/new-api/systems/collision-handler.go b/examples/new-api/systems/collision-handler.go index 60c4768f..0db83b12 100644 --- a/examples/new-api/systems/collision-handler.go +++ b/examples/new-api/systems/collision-handler.go @@ -164,11 +164,11 @@ func (s *CollisionHandlerSystem) checkBulletCollisionEnter(e1, e2 ecs.Entity) bo bulletHp.Hp -= 1 return true } - wallTag := s.WallTags.GetUnsafe(e2) - if wallTag != nil { - bulletHp.Hp = 0 - return true - } + //wallTag := s.WallTags.GetUnsafe(e2) + //if wallTag != nil { + // bulletHp.Hp = 0 + // return true + //} } else if e2Tag != nil { // this is a bullet bulletHp := s.Hps.GetUnsafe(e2) diff --git a/examples/new-api/systems/damping.go b/examples/new-api/systems/damping.go index 40586591..b4b4e67d 100644 --- a/examples/new-api/systems/damping.go +++ b/examples/new-api/systems/damping.go @@ -25,15 +25,15 @@ type DampingSystem struct { func (s *DampingSystem) Init() {} func (s *DampingSystem) Run(dt time.Duration) { - dampingFactor := float32(0.98) // Damping factor for velocity + //dampingFactor := float32(0.98) // Damping factor for velocity s.Velocities.EachEntity()(func(e ecs.Entity) bool { velocity := s.Velocities.GetUnsafe(e) rigidbody := s.RigidBodies.GetUnsafe(e) if rigidbody != nil && !rigidbody.IsStatic { - velocity.X *= dampingFactor - velocity.Y *= dampingFactor + //velocity.X *= dampingFactor + //velocity.Y *= dampingFactor if velocity.X < 0.1 && velocity.X > -0.1 { velocity.X = 0 } diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 1630e1e5..94f6f033 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -142,7 +142,7 @@ func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { c.entityComponentBitSet.Set(entity, c.id) - c.createdEntities.Append(entity) + //c.createdEntities.Append(entity) return component } @@ -221,7 +221,7 @@ func (c *ComponentManager[T]) Delete(entity Entity) { c.lookup.Delete(entity) c.entityComponentBitSet.Unset(entity, c.id) - c.deletedEntities.Append(entity) + //c.deletedEntities.Append(entity) } func (c *ComponentManager[T]) Has(entity Entity) bool { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 01eebb48..703590a7 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -169,10 +169,19 @@ func (a *PagedArray[T]) Last() *T { } func (a *PagedArray[T]) Raw(result []T) []T { - result = result[:0] + totalLen := a.len + // Ensure the result has enough capacity and set the correct length. + if cap(result) < totalLen { + result = make([]T, totalLen) + } else { + result = result[:totalLen] + } + + pos := 0 for i := 0; i <= a.currentPageIndex; i++ { page := &a.book[i] - result = append(result[:i*pageSize], append(result[i*pageSize:], page.data[:page.len]...)...) + n := copy(result[pos:], page.data[:page.len]) + pos += n } return result diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index 85143f55..7cff35e5 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -46,6 +46,8 @@ type BvhTree struct { AabbLeaves ecs.PagedArray[AABB] Codes ecs.PagedArray[uint64] Components ecs.PagedArray[BvhComponent] + + sorted []BvhComponent } func (t *BvhTree) Init() { @@ -158,21 +160,25 @@ func (t *BvhTree) Build() { t.AabbLeaves.Reset() t.Codes.Reset() - var sorted []BvhComponent - sorted = t.Components.Raw(sorted) + if cap(t.sorted) < t.Components.Len() { + t.sorted = make([]BvhComponent, 0, t.Components.Len()) + } + + t.sorted = t.Components.Raw(t.sorted) - slices.SortFunc(sorted, func(a, b BvhComponent) int { + slices.SortFunc(t.sorted, func(a, b BvhComponent) int { return int(a.Code - b.Code) }) // Add leaves - for i := range sorted { - component := sorted[i] + for i := range t.sorted { + component := t.sorted[i] t.Leaves.Append(BvhLeaf{Id: component.Entity}) t.AabbLeaves.Append(component.Aabb) t.Codes.Append(component.Code) } t.Components.Reset() + t.sorted = t.sorted[:0] if t.Leaves.Len() == 0 { return diff --git a/stdcomponents/collision-cell.go b/stdcomponents/collision-cell.go index afd0bf71..51619f09 100644 --- a/stdcomponents/collision-cell.go +++ b/stdcomponents/collision-cell.go @@ -15,14 +15,13 @@ Thank you for your support! package stdcomponents import ( - "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/pkg/worker" ) type CollisionCell struct { - Members ecs.PagedArray[ecs.Entity] - MemberLookup ecs.PagedMap[ecs.Entity, int] + //Members ecs.PagedArray[ecs.Entity] + //MemberLookup ecs.PagedMap[ecs.Entity, int] InputAccumulator []ecs.PagedArray[ecs.Entity] Size float32 @@ -30,8 +29,8 @@ type CollisionCell struct { } func (c *CollisionCell) Init(size float32, layer CollisionLayer, pool *worker.Pool) { - c.Members = ecs.NewPagedArray[ecs.Entity]() - c.MemberLookup = ecs.NewPagedMap[ecs.Entity, int]() + //c.Members = ecs.NewPagedArray[ecs.Entity]() + //c.MemberLookup = ecs.NewPagedMap[ecs.Entity, int]() c.InputAccumulator = make([]ecs.PagedArray[ecs.Entity], pool.NumWorkers()) for i := 0; i < pool.NumWorkers(); i++ { c.InputAccumulator[i] = ecs.NewPagedArray[ecs.Entity]() @@ -40,18 +39,18 @@ func (c *CollisionCell) Init(size float32, layer CollisionLayer, pool *worker.Po c.Layer = layer } -func (c *CollisionCell) AddMember(entity ecs.Entity) { - c.Members.Append(entity) - c.MemberLookup.Set(entity, c.Members.Len()-1) -} - -func (c *CollisionCell) RemoveMember(entity ecs.Entity) { - index, ok := c.MemberLookup.Get(entity) - assert.True(ok) - c.Members.Swap(index, c.Members.Len()-1) - c.Members.SoftReduce() - c.MemberLookup.Delete(entity) -} +//func (c *CollisionCell) AddMember(entity ecs.Entity) { +// c.Members.Append(entity) +// c.MemberLookup.Set(entity, c.Members.Len()-1) +//} +// +//func (c *CollisionCell) RemoveMember(entity ecs.Entity) { +// index, ok := c.MemberLookup.Get(entity) +// assert.True(ok) +// c.Members.Swap(index, c.Members.Len()-1) +// c.Members.SoftReduce() +// c.MemberLookup.Delete(entity) +//} type CollisionCellComponentManager = ecs.ComponentManager[CollisionCell] diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 06acc5cf..1ef014ec 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -57,7 +57,7 @@ type CollisionDetectionSystem struct { func (s *CollisionDetectionSystem) Init() { s.gridLookup = make(map[stdcomponents.CollisionLayer]*stdcomponents.CollisionGrid) s.collisionEventAcc = make([]ecs.PagedArray[CollisionEvent], s.Engine.Pool().NumWorkers()) - for i := 0; i < s.Engine.Pool().NumWorkers(); i++ { + for i := 0; i < len(s.collisionEventAcc); i++ { s.collisionEventAcc[i] = ecs.NewPagedArray[CollisionEvent]() } s.activeCollisions = make(map[CollisionPair]ecs.Entity) @@ -85,6 +85,10 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { s.registerCollisionEvents() s.processExitStates() + + for i := 0; i < len(s.collisionEventAcc); i++ { + s.collisionEventAcc[i].Reset() + } } func (s *CollisionDetectionSystem) Destroy() { s.collisionEventAcc = nil diff --git a/stdsystems/collision-reslution.go b/stdsystems/collision-reslution.go index 57f53363..1698658a 100644 --- a/stdsystems/collision-reslution.go +++ b/stdsystems/collision-reslution.go @@ -15,6 +15,7 @@ Thank you for your support! package stdsystems import ( + "github.com/negrel/assert" "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" @@ -70,11 +71,9 @@ func (s *CollisionResolutionSystem) Run(dt time.Duration) { // Apply impulse resolution velocity1 := s.Velocities.GetUnsafe(collision.E1) + assert.NotNil(velocity1) velocity2 := s.Velocities.GetUnsafe(collision.E2) - - if velocity1 == nil || velocity2 == nil { - return true - } + assert.NotNil(velocity2) relativeVelocity := velocity2.Vec2().Sub(velocity1.Vec2()) velocityAlongNormal := relativeVelocity.Dot(collision.Normal) diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index 8c62bd42..8b895898 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -28,7 +28,7 @@ import ( ) const ( - collidersPerCell = 64 + collidersPerCell = 32 ) func NewCollisionSetupSystem() CollisionSetupSystem { @@ -264,10 +264,10 @@ func (s *CollisionSetupSystem) Run(dt time.Duration) { cell := s.CollisionCellComponentManager.GetUnsafe(cellEntity) assert.NotNil(cell) - _, exists = cell.MemberLookup.Get(entity) - if exists { - return - } + //_, exists = cell.MemberLookup.Get(entity) + //if exists { + // return + //} cell.InputAccumulator[id].Append(entity) }) From 1e609616e9d3ce81f4b3b8c1ca8607edf98997ad Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 24 Apr 2025 22:45:09 +0300 Subject: [PATCH 157/196] fix: value direct memory call --- stdcomponents/bvh-tree.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index 7cff35e5..0ffda015 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -81,10 +81,10 @@ func (t *BvhTree) Query(aabb AABB, result []ecs.Entity) []ecs.Entity { for stackLen > 0 { stackLen-- nodeIndex := int(stack[stackLen]) - a := t.AabbNodes.GetValue(nodeIndex) + a := t.AabbNodes.Get(nodeIndex) // Early exit if no AABB overlap - if !t.aabbOverlap(a, aabb) { + if !t.aabbOverlap(*a, aabb) { continue } @@ -92,8 +92,8 @@ func (t *BvhTree) Query(aabb AABB, result []ecs.Entity) []ecs.Entity { if node.ChildIndex <= 0 { // Is a leaf index := -int(node.ChildIndex) - leafAabb := t.AabbLeaves.GetValue(index) - if t.aabbOverlap(leafAabb, aabb) { + leafAabb := t.AabbLeaves.Get(index) + if t.aabbOverlap(*leafAabb, aabb) { result = append(result, t.Leaves.Get(index).Id) } continue From 21f6bd035c59124d034b860ba12c6d9603c8b978 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 25 Apr 2025 14:19:35 +0300 Subject: [PATCH 158/196] docs - add comments --- pkg/ecs/paged-array.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 703590a7..f4b6326c 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -112,6 +112,7 @@ func (a *PagedArray[T]) Append(values ...T) *T { return result } +// SoftReduce - removes the last element of the array func (a *PagedArray[T]) SoftReduce() { assert.True(a.len > 0, "Len is already 0") @@ -126,6 +127,7 @@ func (a *PagedArray[T]) SoftReduce() { } } +// Reset - resets the array to its initial state func (a *PagedArray[T]) Reset() { for i := 0; i <= a.currentPageIndex; i++ { page := &a.book[i] @@ -191,6 +193,10 @@ func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { return index >> pageSizeShift, index % pageSize } +// ========================= +// Iterators +// ========================= + func (a *PagedArray[T]) Each() func(yield func(int, *T) bool) { return func(yield func(int, *T) bool) { var page *ArrayPage[T] From a08654f2d6c8056b3cfeb10d64c4c2a90794325a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 25 Apr 2025 23:11:24 +0300 Subject: [PATCH 159/196] fix multithreading --- pkg/core/engine.go | 2 +- pkg/worker/pool.go | 2 +- pkg/worker/worker.go | 5 +-- stdcomponents/tint.go | 2 +- stdsystems/collision-setup.go | 5 +-- stdsystems/debug.go | 30 +++++++++++++++++ stdsystems/render-2d-cameras.go | 59 +++++++++++++++++++++++++++++---- taskfile.yml | 2 +- 8 files changed, 93 insertions(+), 14 deletions(-) diff --git a/pkg/core/engine.go b/pkg/core/engine.go index 2e06c2ef..962b61db 100644 --- a/pkg/core/engine.go +++ b/pkg/core/engine.go @@ -26,7 +26,7 @@ const ( ) func NewEngine(game AnyGame) Engine { - numCpu := max(runtime.NumCPU()-2, 1) + numCpu := max(runtime.NumCPU(), 1) engine := Engine{ Game: game, pool: worker.NewPool(numCpu), diff --git a/pkg/worker/pool.go b/pkg/worker/pool.go index c65d9122..3bdf8969 100644 --- a/pkg/worker/pool.go +++ b/pkg/worker/pool.go @@ -28,7 +28,7 @@ import ( func NewPool(n int) Pool { return Pool{ workers: make([]Worker, n), - taskChan: make(chan AnyTask, n), + taskChan: make(chan AnyTask, n*4), } } diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go index 7496970e..fb47adfd 100644 --- a/pkg/worker/worker.go +++ b/pkg/worker/worker.go @@ -16,6 +16,7 @@ package worker import ( "context" + "runtime" "sync" ) @@ -47,8 +48,8 @@ func (w *Worker) Stop() { } func (w *Worker) run(poolWg *sync.WaitGroup) { - //runtime.LockOSThread() - //defer runtime.UnlockOSThread() + runtime.LockOSThread() + defer runtime.UnlockOSThread() defer poolWg.Done() for { select { diff --git a/stdcomponents/tint.go b/stdcomponents/tint.go index 6fd93208..9b26769b 100644 --- a/stdcomponents/tint.go +++ b/stdcomponents/tint.go @@ -19,7 +19,7 @@ import ( "image/color" ) -type Tint = color.RGBA +type Tint = color.RGBA // TODO: remove type alias type TintComponentManager = ecs.ComponentManager[Tint] diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index 8b895898..e8d5b496 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -7,7 +7,8 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- SeniorOverflow Donated 500 RUB +<- SeniorOverflow Donated 1 000 RUB Thank you for your support! */ @@ -28,7 +29,7 @@ import ( ) const ( - collidersPerCell = 32 + collidersPerCell = 8 ) func NewCollisionSetupSystem() CollisionSetupSystem { diff --git a/stdsystems/debug.go b/stdsystems/debug.go index d0165aec..2760de63 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -14,6 +14,7 @@ import ( _ "net/http/pprof" "os" "runtime/pprof" + "runtime/trace" ) const gpprof = false @@ -24,6 +25,8 @@ func NewDebugSystem() DebugSystem { type DebugSystem struct { pprofEnabled bool + traceEnabled bool + traceCounter int } func (s *DebugSystem) Init() { @@ -68,5 +71,32 @@ func (s *DebugSystem) Run() { s.pprofEnabled = !s.pprofEnabled } + + if rl.IsKeyPressed(rl.KeyF10) { + if !s.traceEnabled { + f, err := os.Create("trace.out") + if err != nil { + log.Fatal(err) + } + + err = trace.Start(f) + if err != nil { + log.Fatal(err) + } + log.Println("Trace Profile Started") + + s.traceEnabled = true + } + } + + if s.traceEnabled { + s.traceCounter++ + if s.traceCounter == 10 { + trace.Stop() + s.traceEnabled = false + s.traceCounter = 0 + log.Println("Trace Profile Stopped") + } + } } func (s *DebugSystem) Destroy() {} diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index 6deede64..cce1936c 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -143,12 +143,59 @@ func (s *Render2DCamerasSystem) Destroy() { } func (s *Render2DCamerasSystem) prepareRender(dt time.Duration) { - s.prepareAnimations() - s.prepareFlips() - s.preparePositions(dt) - s.prepareRotations() - s.prepareScales() - s.prepareTints() + s.Textures.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { + texturePro := s.Textures.GetUnsafe(entity) + assert.NotNil(texturePro) + + animation := s.AnimationPlayers.GetUnsafe(entity) + if animation != nil { + frame := &texturePro.Frame + if animation.Vertical { + frame.Y += frame.Height * float32(animation.Current) + } else { + frame.X += frame.Width * float32(animation.Current) + } + } + + flipped := s.Flips.GetUnsafe(entity) + if flipped != nil { + if flipped.X { + texturePro.Frame.Width *= -1 + } + if flipped.Y { + texturePro.Frame.Height *= -1 + } + } + + position := s.Positions.GetUnsafe(entity) + if position != nil { + //decay := 40.0 // DECAY IS TICKRATE DEPENDENT + //x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) + //y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) + texturePro.Dest.X = position.XY.X + texturePro.Dest.Y = position.XY.Y + } + + rotation := s.Rotations.GetUnsafe(entity) + if rotation != nil { + texturePro.Rotation = float32(rotation.Degrees()) + } + + scale := s.Scales.GetUnsafe(entity) + if scale != nil { + texturePro.Dest.Width *= scale.XY.X + texturePro.Dest.Height *= scale.XY.Y + } + + tint := s.Tints.GetUnsafe(entity) + if tint != nil { + trTint := &texturePro.Tint + trTint.A = tint.A + trTint.R = tint.R + trTint.G = tint.G + trTint.B = tint.B + } + }) } func (s *Render2DCamerasSystem) prepareAnimations() { diff --git a/taskfile.yml b/taskfile.yml index e882d40b..4ca098f4 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -46,7 +46,7 @@ tasks: pprof-trace: cmds: - - go tool trace trace.out + - go tool trace -http='127.0.0.1:8002' ./trace.out build-sdl: env: From 75fb158cba63aa5b4e9387f36c9bf7a1baf36d11 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 26 Apr 2025 02:41:06 +0500 Subject: [PATCH 160/196] fewer allocations --- pkg/bvh/tree.go | 18 +++--- pkg/ecs/component-bit-table.go | 81 +++++++++++++++++++++++++++ pkg/ecs/component-manager-shared.go | 24 ++++---- pkg/ecs/component-manager.go | 22 ++++---- pkg/ecs/entity-manager.go | 14 ++--- pkg/ecs/paged-array.go | 24 +++++++- stdcomponents/bvh-tree.go | 18 +++--- stdcomponents/collision-cell.go | 2 +- stdcomponents/collision-grid.go | 2 +- stdsystems/bvh-tree.go | 16 +++--- stdsystems/collision-detection-bvh.go | 4 +- stdsystems/collision-detection.go | 4 +- stdsystems/collision-setup.go | 4 +- 13 files changed, 171 insertions(+), 62 deletions(-) create mode 100644 pkg/ecs/component-bit-table.go diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 17ac0be7..2179501c 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -65,7 +65,7 @@ type Tree struct { func (t *Tree) AddComponent(entity ecs.Entity, aabb stdcomponents.AABB) { code := t.morton2D(&aabb) - t.components.Append(component{ + t.components.AppendOne(component{ entity: entity, aabb: aabb, code: code, @@ -94,9 +94,9 @@ func (t *Tree) Build() { // Add leaves for i := range t.componentsSlice { component := &t.componentsSlice[i] - t.leaves.Append(leaf{id: component.entity}) - t.AabbLeaves.Append(component.aabb) - t.codes.Append(component.code) + t.leaves.AppendOne(leaf{id: component.entity}) + t.AabbLeaves.AppendOne(component.aabb) + t.codes.AppendOne(component.code) } t.components.Reset() @@ -105,8 +105,8 @@ func (t *Tree) Build() { } // Add root node - t.nodes.Append(node{-1}) - t.AabbNodes.Append(stdcomponents.AABB{}) + t.nodes.AppendOne(node{-1}) + t.AabbNodes.AppendOne(stdcomponents.AABB{}) type buildTask struct { parentIndex int @@ -137,8 +137,10 @@ func (t *Tree) Build() { // Create left and right nodes leftIndex := t.nodes.Len() - t.nodes.Append(node{-1}, node{-1}) - t.AabbNodes.Append(stdcomponents.AABB{}, stdcomponents.AABB{}) + t.nodes.AppendOne(node{-1}) + t.nodes.AppendOne(node{-1}) + t.AabbNodes.AppendOne(stdcomponents.AABB{}) + t.AabbNodes.AppendOne(stdcomponents.AABB{}) // Set parent's childIndex to leftIndex t.nodes.Get(task.parentIndex).childIndex = int32(leftIndex) diff --git a/pkg/ecs/component-bit-table.go b/pkg/ecs/component-bit-table.go new file mode 100644 index 00000000..6e1310f0 --- /dev/null +++ b/pkg/ecs/component-bit-table.go @@ -0,0 +1,81 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "github.com/negrel/assert" + "math/bits" +) + +const ComponentBitTablePreallocate = 1024 + +func NewComponentBitTable(maxComponentsLen int) ComponentBitTable { + bitsetSize := (maxComponentsLen / bits.UintSize) + 1 + return ComponentBitTable{ + bits: make([]uint, bitsetSize, bitsetSize*ComponentBitTablePreallocate), + lookup: make(map[Entity]int, ComponentBitTablePreallocate), + bitsetSize: bitsetSize, + } +} + +type ComponentBitTable struct { + bits []uint + lookup map[Entity]int + bitsetSize int +} + +// Set sets the bit at the given index to 1. +func (b *ComponentBitTable) Set(entity Entity, componentId ComponentId) { + bitsId, ok := b.lookup[entity] + if !ok { // Most likely the entity is new + b.extend() + bitsId = len(b.bits) + b.lookup[entity] = bitsId + b.bits = append(b.bits, make([]uint, b.bitsetSize)...) + } + + offset := int(componentId / bits.UintSize) + b.bits[bitsId+offset] |= 1 << (componentId % bits.UintSize) +} + +// Unset clears the bit at the given index (sets it to 0). +func (b *ComponentBitTable) Unset(entity Entity, componentId ComponentId) { + bitsId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + offset := int(componentId / bits.UintSize) + b.bits[bitsId+offset] &= ^(1 << (componentId % bits.UintSize)) +} + +func (b *ComponentBitTable) extend() { + if len(b.bits) == cap(b.bits) { + b.bits = append(b.bits, make([]uint, cap(b.bits)*2)...) + } +} + +func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) { + bitsId, ok := b.lookup[entity] + if !ok { + return + } + for i := 0; i < b.bitsetSize; i++ { + for j := 0; j < bits.UintSize; j++ { + if (b.bits[bitsId+i]>>j)&1 == 1 { + if !yield(ComponentId(i*bits.UintSize + j)) { + return + } + } + } + } +} diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 4dbe110e..06ea8b04 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -51,9 +51,9 @@ type SharedComponentManager[T any] struct { references PagedArray[SharedComponentInstanceId] lookup PagedMap[Entity, int] - entityManager *EntityManager - entityComponentBitSet *ComponentBitSet - pool *worker.Pool + entityManager *EntityManager + entityComponentBitTable *ComponentBitTable + pool *worker.Pool id ComponentId isInitialized bool @@ -99,7 +99,7 @@ func (c *SharedComponentManager[T]) Id() ComponentId { func (c *SharedComponentManager[T]) registerEntityManager(entityManager *EntityManager) { c.entityManager = entityManager - c.entityComponentBitSet = &entityManager.componentBitSet + c.entityComponentBitTable = &entityManager.componentBitTable } //===================================== @@ -115,8 +115,8 @@ func (c *SharedComponentManager[T]) Create(instanceId SharedComponentInstanceId, defer c.assertEnd() componentIndex := c.components.Len() - component := c.components.Append(value) - c.instances.Append(instanceId) + component := c.components.AppendOne(value) + c.instances.AppendOne(instanceId) c.instanceToComponent.Set(instanceId, componentIndex) return component @@ -159,14 +159,14 @@ func (c *SharedComponentManager[T]) Set(entity Entity, instanceId SharedComponen c.references.Set(index, instanceId) } else { newIndex := c.entities.Len() - c.entities.Append(entity) - c.references.Append(instanceId) + c.entities.AppendOne(entity) + c.references.AppendOne(instanceId) c.lookup.Set(entity, newIndex) } componentIndex, _ := c.instanceToComponent.Get(instanceId) c.entityToComponent.Set(entity, componentIndex) - c.patchedEntities.Append(entity) - c.entityComponentBitSet.Set(entity, c.id) + c.patchedEntities.AppendOne(entity) + c.entityComponentBitTable.Set(entity, c.id) return c.components.Get(componentIndex) } @@ -196,9 +196,9 @@ func (c *SharedComponentManager[T]) Delete(entity Entity) { c.lookup.Delete(entity) c.entityToComponent.Delete(entity) - c.entityComponentBitSet.Unset(entity, c.id) + c.entityComponentBitTable.Unset(entity, c.id) - c.deletedEntities.Append(entity) + c.deletedEntities.AppendOne(entity) } func (c *SharedComponentManager[T]) Has(entity Entity) bool { diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 94f6f033..b1f4462b 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -73,8 +73,8 @@ type ComponentManager[T any] struct { entities PagedArray[Entity] lookup PagedMap[Entity, int] - entityManager *EntityManager - entityComponentBitSet *ComponentBitSet + entityManager *EntityManager + entityComponentBitTable *ComponentBitTable id ComponentId isInitialized bool @@ -115,7 +115,7 @@ func (c *ComponentManager[T]) Id() ComponentId { func (c *ComponentManager[T]) registerEntityManager(entityManager *EntityManager) { c.entityManager = entityManager - c.entityComponentBitSet = &entityManager.componentBitSet + c.entityComponentBitTable = &entityManager.componentBitTable } func (c *ComponentManager[T]) registerWorkerPool(pool *worker.Pool) { @@ -137,12 +137,12 @@ func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { var index = c.components.Len() c.lookup.Set(entity, index) - c.entities.Append(entity) - component = c.components.Append(value) + c.entities.AppendOne(entity) + component = c.components.AppendOne(value) - c.entityComponentBitSet.Set(entity, c.id) + c.entityComponentBitTable.Set(entity, c.id) - //c.createdEntities.Append(entity) + //c.createdEntities.AppendOne(entity) return component } @@ -188,7 +188,7 @@ func (c *ComponentManager[T]) Set(entity Entity, value T) *T { component := c.components.Set(index, value) - c.patchedEntities.Append(entity) + c.patchedEntities.AppendOne(entity) return component } @@ -219,9 +219,9 @@ func (c *ComponentManager[T]) Delete(entity Entity) { c.entities.SoftReduce() c.lookup.Delete(entity) - c.entityComponentBitSet.Unset(entity, c.id) + c.entityComponentBitTable.Unset(entity, c.id) - //c.deletedEntities.Append(entity) + //c.deletedEntities.AppendOne(entity) } func (c *ComponentManager[T]) Has(entity Entity) bool { @@ -304,7 +304,7 @@ func (c *ComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entit func (c *ComponentManager[T]) PatchAdd(entity Entity) { assert.True(c.TrackChanges) - c.patchedEntities.Append(entity) + c.patchedEntities.AppendOne(entity) } func (c *ComponentManager[T]) PatchGet() ComponentPatch { diff --git a/pkg/ecs/entity-manager.go b/pkg/ecs/entity-manager.go index eb2bf1ad..2dbdc4fa 100644 --- a/pkg/ecs/entity-manager.go +++ b/pkg/ecs/entity-manager.go @@ -60,11 +60,11 @@ type EntityManager struct { lastId Entity size uint32 - groups map[string][]Entity - components map[ComponentId]AnyComponentManagerPtr - deletedEntityIDs []Entity - componentBitSet ComponentBitSet - mx sync.Mutex + groups map[string][]Entity + components map[ComponentId]AnyComponentManagerPtr + deletedEntityIDs []Entity + componentBitTable ComponentBitTable + mx sync.Mutex patch Patch } @@ -84,7 +84,7 @@ func (e *EntityManager) Create() Entity { func (e *EntityManager) Delete(entity Entity) { e.mx.Lock() defer e.mx.Unlock() - e.componentBitSet.AllSet(entity, func(id ComponentId) bool { + e.componentBitTable.AllSet(entity, func(id ComponentId) bool { e.components[id].Delete(entity) return true }) @@ -155,7 +155,7 @@ func (e *EntityManager) PatchReset() { } func (e *EntityManager) init() { - e.componentBitSet = NewComponentBitSet() + e.componentBitTable = NewComponentBitTable(len(e.components)) e.patch = make(Patch, len(e.components)) } diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 703590a7..c8458457 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -86,7 +86,29 @@ func (a *PagedArray[T]) extend() { a.edpTasks = append(a.edpTasks, newEdpTasks...) } -func (a *PagedArray[T]) Append(values ...T) *T { +func (a *PagedArray[T]) AppendOne(value T) *T { + var result *T + if a.currentPageIndex >= len(a.book) { + a.extend() + } + + page := &a.book[a.currentPageIndex] + + if page.len == pageSize { + a.currentPageIndex++ + if a.currentPageIndex >= len(a.book) { + a.extend() + } + page = &a.book[a.currentPageIndex] + } + page.data[page.len] = value + result = &page.data[page.len] + page.len++ + a.len++ + return result +} + +func (a *PagedArray[T]) AppendMany(values ...T) *T { var result *T for i := range values { value := values[i] diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index 0ffda015..17667f3f 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -61,7 +61,7 @@ func (t *BvhTree) Init() { func (t *BvhTree) AddComponent(entity ecs.Entity, aabb AABB) { code := t.morton2D(&aabb) - t.Components.Append(BvhComponent{ + t.Components.AppendOne(BvhComponent{ Entity: entity, Aabb: aabb, Code: code, @@ -173,9 +173,9 @@ func (t *BvhTree) Build() { // Add leaves for i := range t.sorted { component := t.sorted[i] - t.Leaves.Append(BvhLeaf{Id: component.Entity}) - t.AabbLeaves.Append(component.Aabb) - t.Codes.Append(component.Code) + t.Leaves.AppendOne(BvhLeaf{Id: component.Entity}) + t.AabbLeaves.AppendOne(component.Aabb) + t.Codes.AppendOne(component.Code) } t.Components.Reset() t.sorted = t.sorted[:0] @@ -185,8 +185,8 @@ func (t *BvhTree) Build() { } // Add root node - t.Nodes.Append(BvhNode{-1}) - t.AabbNodes.Append(AABB{}) + t.Nodes.AppendOne(BvhNode{-1}) + t.AabbNodes.AppendOne(AABB{}) type buildTask struct { parentIndex int @@ -217,8 +217,10 @@ func (t *BvhTree) Build() { // Create left and right nodes leftIndex := t.Nodes.Len() - t.Nodes.Append(BvhNode{-1}, BvhNode{-1}) - t.AabbNodes.Append(AABB{}, AABB{}) + t.Nodes.AppendOne(BvhNode{-1}) + t.Nodes.AppendOne(BvhNode{-1}) + t.AabbNodes.AppendOne(AABB{}) + t.AabbNodes.AppendOne(AABB{}) // Set parent's childIndex to leftIndex t.Nodes.Get(task.parentIndex).ChildIndex = int32(leftIndex) diff --git a/stdcomponents/collision-cell.go b/stdcomponents/collision-cell.go index 51619f09..b0e55d7a 100644 --- a/stdcomponents/collision-cell.go +++ b/stdcomponents/collision-cell.go @@ -40,7 +40,7 @@ func (c *CollisionCell) Init(size float32, layer CollisionLayer, pool *worker.Po } //func (c *CollisionCell) AddMember(entity ecs.Entity) { -// c.Members.Append(entity) +// c.Members.AppendOne(entity) // c.MemberLookup.Set(entity, c.Members.Len()-1) //} // diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go index 1fe921ee..6bd1129d 100644 --- a/stdcomponents/collision-grid.go +++ b/stdcomponents/collision-grid.go @@ -72,7 +72,7 @@ func (g *CollisionGrid) Query(bb AABB, result []ecs.Entity) []ecs.Entity { } func (g *CollisionGrid) RegisterEntity(entity ecs.Entity, aabb *AABB) { - g.Entities.Append(entity) + g.Entities.AppendOne(entity) l := aabb.Max.Sub(aabb.Min) diff --git a/stdsystems/bvh-tree.go b/stdsystems/bvh-tree.go index baf6a22a..9ead4321 100644 --- a/stdsystems/bvh-tree.go +++ b/stdsystems/bvh-tree.go @@ -54,9 +54,9 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { // Add leaves for i := range sorted { component := sorted[i] - t.Leaves.Append(stdcomponents.BvhLeaf{Id: component.Entity}) - t.AabbLeaves.Append(component.Aabb) - t.Codes.Append(component.Code) + t.Leaves.AppendOne(stdcomponents.BvhLeaf{Id: component.Entity}) + t.AabbLeaves.AppendOne(component.Aabb) + t.Codes.AppendOne(component.Code) } t.Components.Reset() @@ -65,8 +65,8 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { } // Add root node - t.Nodes.Append(stdcomponents.BvhNode{-1}) - t.AabbNodes.Append(stdcomponents.AABB{}) + t.Nodes.AppendOne(stdcomponents.BvhNode{-1}) + t.AabbNodes.AppendOne(stdcomponents.AABB{}) type buildTask struct { parentIndex int @@ -96,8 +96,10 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { // Create left and right nodes leftIndex := t.Nodes.Len() - t.Nodes.Append(stdcomponents.BvhNode{-1}, stdcomponents.BvhNode{-1}) - t.AabbNodes.Append(stdcomponents.AABB{}, stdcomponents.AABB{}) + t.Nodes.AppendOne(stdcomponents.BvhNode{-1}) + t.Nodes.AppendOne(stdcomponents.BvhNode{-1}) + t.AabbNodes.AppendOne(stdcomponents.AABB{}) + t.AabbNodes.AppendOne(stdcomponents.AABB{}) // Set parent's childIndex to leftIndex t.Nodes.Get(task.parentIndex).ChildIndex = int32(leftIndex) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 10cbfcff..dc0815cc 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -221,7 +221,7 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE radiusA := circleA.Radius * scaleA.XY.X radiusB := circleB.Radius * scaleB.XY.X if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { - s.collisionEvents[workerId].Append(CollisionEvent{ + s.collisionEvents[workerId].AppendOne(CollisionEvent{ entityA: entityA, entityB: entityB, position: transformA.Position, @@ -244,7 +244,7 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE // If collision detected, get penetration details using EPA normal, depth := test.EPA(colA, colB, transformA, transformB) position := posA.XY.Add(posB.XY.Sub(posA.XY)) - s.collisionEvents[workerId].Append(CollisionEvent{ + s.collisionEvents[workerId].AppendOne(CollisionEvent{ entityA: entityA, entityB: entityB, position: position, diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 1ef014ec..48511e6e 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -166,7 +166,7 @@ func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEnti radiusA := circleA.Radius * scaleA.XY.X radiusB := circleB.Radius * scaleB.XY.X if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { - s.collisionEventAcc[workerId].Append(CollisionEvent{ + s.collisionEventAcc[workerId].AppendOne(CollisionEvent{ entityA: entityA, entityB: entityB, position: transformA.Position, @@ -189,7 +189,7 @@ func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEnti // If collision detected, get penetration details using EPA normal, depth := test.EPA(colA, colB, transformA, transformB) position := posA.XY.Add(posB.XY.Sub(posA.XY)) - s.collisionEventAcc[workerId].Append(CollisionEvent{ + s.collisionEventAcc[workerId].AppendOne(CollisionEvent{ entityA: entityA, entityB: entityB, position: position, diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index 8b895898..7a60bb04 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -215,7 +215,7 @@ func (s *CollisionSetupSystem) Run(dt time.Duration) { for i := range grid.CellAccumulator { for spatialIndex := range grid.CellAccumulator[i] { cellEntity := s.EntityManager.Create() - grid.Cells.Append(cellEntity) + grid.Cells.AppendOne(cellEntity) grid.CellLookup[spatialIndex] = grid.Cells.Len() - 1 collisionCell := s.CollisionCellComponentManager.Create(cellEntity, stdcomponents.CollisionCell{}) collisionCell.Init(grid.CellSize, grid.Layer, s.Engine.Pool()) @@ -268,7 +268,7 @@ func (s *CollisionSetupSystem) Run(dt time.Duration) { //if exists { // return //} - cell.InputAccumulator[id].Append(entity) + cell.InputAccumulator[id].AppendOne(entity) }) //Build grid cells From 17ae43ada746a892358ef9b8e298d372dbea23fb Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 26 Apr 2025 11:15:41 +0500 Subject: [PATCH 161/196] feat: chunked bit table add: tests --- pkg/ecs/component-bit-table.go | 43 +++-- pkg/ecs/component-bit-table_test.go | 280 ++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+), 11 deletions(-) create mode 100644 pkg/ecs/component-bit-table_test.go diff --git a/pkg/ecs/component-bit-table.go b/pkg/ecs/component-bit-table.go index 6e1310f0..3fadc0da 100644 --- a/pkg/ecs/component-bit-table.go +++ b/pkg/ecs/component-bit-table.go @@ -20,47 +20,66 @@ import ( ) const ComponentBitTablePreallocate = 1024 +const chunkBaseSize = ComponentBitTablePreallocate +const chunkPreallocate = 2 func NewComponentBitTable(maxComponentsLen int) ComponentBitTable { - bitsetSize := (maxComponentsLen / bits.UintSize) + 1 + bitsetSize := ((maxComponentsLen - 1) / bits.UintSize) + 1 return ComponentBitTable{ - bits: make([]uint, bitsetSize, bitsetSize*ComponentBitTablePreallocate), + bits: make([][]uint, 0, chunkPreallocate), lookup: make(map[Entity]int, ComponentBitTablePreallocate), bitsetSize: bitsetSize, } } type ComponentBitTable struct { - bits []uint + bits [][]uint lookup map[Entity]int + length int bitsetSize int } // Set sets the bit at the given index to 1. func (b *ComponentBitTable) Set(entity Entity, componentId ComponentId) { bitsId, ok := b.lookup[entity] - if !ok { // Most likely the entity is new + if !ok { b.extend() - bitsId = len(b.bits) + bitsId = b.length b.lookup[entity] = bitsId - b.bits = append(b.bits, make([]uint, b.bitsetSize)...) + b.length += b.bitsetSize } + chunkId := bitsId / chunkBaseSize + bitsetId := bitsId % chunkBaseSize offset := int(componentId / bits.UintSize) - b.bits[bitsId+offset] |= 1 << (componentId % bits.UintSize) + b.bits[chunkId][bitsetId+offset] |= 1 << (componentId % bits.UintSize) } // Unset clears the bit at the given index (sets it to 0). func (b *ComponentBitTable) Unset(entity Entity, componentId ComponentId) { bitsId, ok := b.lookup[entity] assert.True(ok, "entity not found") + chunkId := bitsId / chunkBaseSize + bitsetId := bitsId % chunkBaseSize + offset := int(componentId / bits.UintSize) + b.bits[chunkId][bitsetId+offset] &= ^(1 << (componentId % bits.UintSize)) +} + +func (b *ComponentBitTable) Test(entity Entity, componentId ComponentId) bool { + bitsId, ok := b.lookup[entity] + if !ok { + return false + } + chunkId := bitsId / chunkBaseSize + bitsetId := bitsId % chunkBaseSize offset := int(componentId / bits.UintSize) - b.bits[bitsId+offset] &= ^(1 << (componentId % bits.UintSize)) + return (b.bits[chunkId][bitsetId+offset] & (1 << (componentId % bits.UintSize))) != 0 } func (b *ComponentBitTable) extend() { - if len(b.bits) == cap(b.bits) { - b.bits = append(b.bits, make([]uint, cap(b.bits)*2)...) + lastChunkId := b.length / chunkBaseSize + if lastChunkId == len(b.bits) && b.length%chunkBaseSize == 0 { + b.bits = append(b.bits, make([]uint, b.bitsetSize*chunkBaseSize)) } } @@ -69,9 +88,11 @@ func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) if !ok { return } + chunkId := bitsId / chunkBaseSize + bitsetId := bitsId % chunkBaseSize for i := 0; i < b.bitsetSize; i++ { for j := 0; j < bits.UintSize; j++ { - if (b.bits[bitsId+i]>>j)&1 == 1 { + if (b.bits[chunkId][bitsetId+i]>>j)&1 == 1 { if !yield(ComponentId(i*bits.UintSize + j)) { return } diff --git a/pkg/ecs/component-bit-table_test.go b/pkg/ecs/component-bit-table_test.go new file mode 100644 index 00000000..eada65b5 --- /dev/null +++ b/pkg/ecs/component-bit-table_test.go @@ -0,0 +1,280 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "math/bits" + "testing" +) + +func TestNewComponentBitTable(t *testing.T) { + // Test with different max component sizes + tests := []struct { + name string + maxComponentsLen int + expectedBitsetSize int + }{ + {"Small", 10, 1}, + {"Medium", 64, 1}, + {"Large", 192, 3}, + {"Below", 172, 3}, + {"Above", 200, 4}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + table := NewComponentBitTable(tt.maxComponentsLen) + + if table.bitsetSize != tt.expectedBitsetSize { + t.Errorf("Expected bitsetSize %d, got %d", tt.expectedBitsetSize, table.bitsetSize) + } + + if cap(table.bits) != chunkPreallocate { + t.Errorf("Expected %d preallocated chunks, got %d", chunkPreallocate, cap(table.bits)) + } + }) + } +} + +func TestComponentBitTable_Set(t *testing.T) { + table := NewComponentBitTable(100) + + // Set a bit for a new entity + entity1 := Entity(1) + table.Set(entity1, ComponentId(5)) + + // Verify bit was set + bitsId, ok := table.lookup[entity1] + if !ok { + t.Fatalf("Entity %d not found in lookup", entity1) + } + + chunkId := bitsId / chunkBaseSize + bitsetId := bitsId % chunkBaseSize + offset := int(ComponentId(5) / bits.UintSize) + mask := uint(1 << (ComponentId(5) % bits.UintSize)) + + if (table.bits[chunkId][bitsetId+offset] & mask) == 0 { + t.Errorf("Expected bit to be set for entity %d, component %d", entity1, 5) + } + + // Set multiple bits for same entity + table.Set(entity1, ComponentId(10)) + table.Set(entity1, ComponentId(63)) + table.Set(entity1, ComponentId(64)) + + // Set bits for a different entity + entity2 := Entity(2) + table.Set(entity2, ComponentId(5)) +} + +func TestComponentBitTable_Unset(t *testing.T) { + table := NewComponentBitTable(100) + entity := Entity(1) + + // Set and then unset + table.Set(entity, ComponentId(5)) + table.Set(entity, ComponentId(10)) + table.Unset(entity, ComponentId(5)) + + // Verify bit was unset + bitsId := table.lookup[entity] + chunkId := bitsId / chunkBaseSize + bitsetId := bitsId % chunkBaseSize + offset := int(ComponentId(5) / bits.UintSize) + mask := uint(1 << (ComponentId(5) % bits.UintSize)) + + if (table.bits[chunkId][bitsetId+offset] & mask) != 0 { + t.Errorf("Expected bit to be unset for entity %d, component %d", entity, 5) + } + + // Verify other bit is still set + offset = int(ComponentId(10) / bits.UintSize) + mask = uint(1 << (ComponentId(10) % bits.UintSize)) + + if (table.bits[chunkId][bitsetId+offset] & mask) == 0 { + t.Errorf("Expected bit to still be set for entity %d, component %d", entity, 10) + } +} + +func TestComponentBitTable_Test(t *testing.T) { + table := NewComponentBitTable(100) + + // Test for non-existent entity + if table.Test(Entity(999), ComponentId(5)) { + t.Error("Test should return false for non-existent entity") + } + + // Set up an entity with some components + entity := Entity(42) + table.Set(entity, ComponentId(5)) + table.Set(entity, ComponentId(64)) + + // Test for set components + if !table.Test(entity, ComponentId(5)) { + t.Error("Test should return true for set component 5") + } + if !table.Test(entity, ComponentId(64)) { + t.Error("Test should return true for set component 64") + } + + // Test for unset component + if table.Test(entity, ComponentId(10)) { + t.Error("Test should return false for unset component 10") + } + + // Test after unsetting a component + table.Unset(entity, ComponentId(5)) + if table.Test(entity, ComponentId(5)) { + t.Error("Test should return false after component is unset") + } + + // Test components at boundaries + entity2 := Entity(43) + table.Set(entity2, ComponentId(0)) + table.Set(entity2, ComponentId(64)) // Last bit in first uint + table.Set(entity2, ComponentId(65)) // First bit in second uint + + if !table.Test(entity2, ComponentId(0)) { + t.Error("Test should return true for set component at uint boundary (0)") + } + if !table.Test(entity2, ComponentId(64)) { + t.Error("Test should return true for set component at uint boundary (64)") + } + if !table.Test(entity2, ComponentId(65)) { + t.Error("Test should return true for set component at uint boundary (65)") + } +} + +func TestComponentBitTable_AllSet(t *testing.T) { + table := NewComponentBitTable(200) + entity := Entity(1) + + // Set several components + expectedComponents := []ComponentId{5, 10, 64, 128, 199} + for _, id := range expectedComponents { + table.Set(entity, id) + } + + // Use AllSet to collect components + var foundComponents []ComponentId + table.AllSet(entity, func(id ComponentId) bool { + foundComponents = append(foundComponents, id) + return true + }) + + // Verify all components were found + if len(foundComponents) != len(expectedComponents) { + t.Errorf("Expected %d components, found %d", len(expectedComponents), len(foundComponents)) + } + + // Check each component + componentMap := make(map[ComponentId]bool) + for _, id := range foundComponents { + componentMap[id] = true + } + + for _, id := range expectedComponents { + if !componentMap[id] { + t.Errorf("Component %d not found in AllSet results", id) + } + } + + // Test early termination + count := 0 + table.AllSet(entity, func(id ComponentId) bool { + count++ + return count < 3 // Stop after finding 2 components + }) + + if count != 3 { + t.Errorf("Early termination didn't work as expected. Count: %d", count) + } +} + +func TestComponentBitTable_extend(t *testing.T) { + // Create a table with a small chunk size for testing + table := NewComponentBitTable(20) + // Set bits to force extension + for i := 0; i < chunkBaseSize*table.bitsetSize; i++ { + table.Set(Entity(i), ComponentId(1)) + } + + if len(table.bits) > 1 { + t.Errorf("Expected table to be not extended, got %d chunks", len(table.bits)) + } + + table.Set(Entity(chunkBaseSize*table.bitsetSize), ComponentId(1)) + + if len(table.bits) != 2 { + t.Errorf("Expected table to extend up to 2 chunks, got %d chunks", len(table.bits)) + } + + for i := chunkBaseSize * table.bitsetSize; i < chunkBaseSize*table.bitsetSize*2; i++ { + table.Set(Entity(i), ComponentId(1)) + } + + if len(table.bits) != 2 { + t.Errorf("Expected table to extend up to 2 chunks, got %d chunks", len(table.bits)) + } + + table.Set(Entity(chunkBaseSize*table.bitsetSize*2), ComponentId(1)) + + if len(table.bits) != 3 { + t.Errorf("Expected table to extend up to 3 chunks, got %d chunks", len(table.bits)) + } +} + +func TestComponentBitTable_EdgeCases(t *testing.T) { + table := NewComponentBitTable(100) + + // Test AllSet on non-existent entity + called := false + table.AllSet(Entity(999), func(id ComponentId) bool { + called = true + return true + }) + + if called { + t.Errorf("AllSet callback should not be called for non-existent entity") + } + + // Test setting across bit boundaries + entity := Entity(42) + table.Set(entity, ComponentId(0)) // Last bit in first uint + table.Set(entity, ComponentId(64)) // Last bit in first uint + table.Set(entity, ComponentId(65)) // First bit in second uint + + // Verify both bits + var found []ComponentId + table.AllSet(entity, func(id ComponentId) bool { + found = append(found, id) + return true + }) + + if len(found) != 3 || !contains(found, ComponentId(0)) || !contains(found, ComponentId(64)) || !contains(found, ComponentId(65)) { + t.Errorf("Expected components 0, 64 and 65, got %v", found) + } +} + +// Helper function to check if slice contains a value +func contains(s []ComponentId, id ComponentId) bool { + for _, v := range s { + if v == id { + return true + } + } + return false +} From 643fe0d5557f9bac3cf8b1ea0c401ca1dc219f55 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 26 Apr 2025 15:41:22 +0300 Subject: [PATCH 162/196] feat i <3 my twitch chat babies --- race_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 race_test.go diff --git a/race_test.go b/race_test.go new file mode 100644 index 00000000..2e8590bc --- /dev/null +++ b/race_test.go @@ -0,0 +1,66 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import ( + "runtime" + "sync" + "testing" +) + +func TestRaceCondition(t *testing.T) { + var ( + numGoroutines = runtime.NumCPU() // Количество горутин + sliceSize = runtime.NumCPU() // Размер слайса (меньше numGoroutines) + ) + slice := make([]float32, sliceSize) + + var wg sync.WaitGroup + wg.Add(numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(id int) { + defer wg.Done() + index := id % sliceSize // Индекс может повторяться + slice[index] = float32(id) + }(i) + } + + wg.Wait() +} + +// СТЕНА ПОЗОРА MaxHero90@twitch - 15 лет опыта работы гофером и все псу под нос +func TestRaceConditionMaxHero90(t *testing.T) { + var ( + numGoroutines = 2 // Количество горутин + sliceSize = 16 // Размер слайса (меньше numGoroutines) + ) + slice := make([]float32, sliceSize) + + var wg sync.WaitGroup + wg.Add(numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(id int) { + defer wg.Done() + for j := id; j < len(slice); j += 2 { + slice[j] = float32(id) + } + }(i) + } + + wg.Wait() + t.Log(slice) +} From 25dbcc50e0fd2c25283fe96a8e9d1d476828542e Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 26 Apr 2025 15:52:37 +0300 Subject: [PATCH 163/196] update race_test.go --- race_test.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/race_test.go b/race_test.go index 2e8590bc..ba843009 100644 --- a/race_test.go +++ b/race_test.go @@ -15,15 +15,14 @@ Thank you for your support! package gomp import ( - "runtime" "sync" "testing" ) func TestRaceCondition(t *testing.T) { var ( - numGoroutines = runtime.NumCPU() // Количество горутин - sliceSize = runtime.NumCPU() // Размер слайса (меньше numGoroutines) + numGoroutines = 10000 // Количество горутин + sliceSize = 10000 // Размер слайса (меньше numGoroutines) ) slice := make([]float32, sliceSize) @@ -39,13 +38,19 @@ func TestRaceCondition(t *testing.T) { } wg.Wait() + for i := 0; i < sliceSize; i++ { + if slice[i] != float32(i) { + t.Errorf("slice[%d] = %f, want %f", i, slice[i], float32(i)) + } + } + t.Log(slice) } // СТЕНА ПОЗОРА MaxHero90@twitch - 15 лет опыта работы гофером и все псу под нос func TestRaceConditionMaxHero90(t *testing.T) { var ( - numGoroutines = 2 // Количество горутин - sliceSize = 16 // Размер слайса (меньше numGoroutines) + numGoroutines = 2 // Количество горутин + sliceSize = 100000 // Размер слайса (меньше numGoroutines) ) slice := make([]float32, sliceSize) @@ -62,5 +67,10 @@ func TestRaceConditionMaxHero90(t *testing.T) { } wg.Wait() + for i := 0; i < sliceSize; i++ { + if slice[i] != float32(i%2) { + t.Errorf("slice[%d] = %f, want %f", i, slice[i], float32(i)) + } + } t.Log(slice) } From 275b015779828370675548df700b273244868bb3 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 26 Apr 2025 16:55:28 +0300 Subject: [PATCH 164/196] update race_test.go --- race_test.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/race_test.go b/race_test.go index ba843009..4c1365da 100644 --- a/race_test.go +++ b/race_test.go @@ -26,18 +26,18 @@ func TestRaceCondition(t *testing.T) { ) slice := make([]float32, sliceSize) - var wg sync.WaitGroup - wg.Add(numGoroutines) + //var wg sync.WaitGroup + //wg.Add(numGoroutines) for i := 0; i < numGoroutines; i++ { go func(id int) { - defer wg.Done() + //defer wg.Done() index := id % sliceSize // Индекс может повторяться slice[index] = float32(id) }(i) } - wg.Wait() + //wg.Wait() for i := 0; i < sliceSize; i++ { if slice[i] != float32(i) { t.Errorf("slice[%d] = %f, want %f", i, slice[i], float32(i)) @@ -46,31 +46,32 @@ func TestRaceCondition(t *testing.T) { t.Log(slice) } -// СТЕНА ПОЗОРА MaxHero90@twitch - 15 лет опыта работы гофером и все псу под нос +// СТЕНА Опыта MaxHero90@twitch - 7 лет опыта работы гофером без использования waitGroup научили его наконец ценить эту волшебную атомик структуру богов. func TestRaceConditionMaxHero90(t *testing.T) { var ( numGoroutines = 2 // Количество горутин sliceSize = 100000 // Размер слайса (меньше numGoroutines) ) slice := make([]float32, sliceSize) + slicePtr := &slice var wg sync.WaitGroup wg.Add(numGoroutines) for i := 0; i < numGoroutines; i++ { - go func(id int) { + go func(id int, s *[]float32) { defer wg.Done() - for j := id; j < len(slice); j += 2 { - slice[j] = float32(id) + for j := id; j < len(*s); j += 2 { + (*s)[j] = float32(id) } - }(i) + }(i, slicePtr) } - wg.Wait() + wg.Wait() // this do not cause a race condition + // time.Sleep(time.Second * 3) - this cause a race condition for i := 0; i < sliceSize; i++ { - if slice[i] != float32(i%2) { - t.Errorf("slice[%d] = %f, want %f", i, slice[i], float32(i)) + if (*slicePtr)[i] != float32(i%2) { + t.Errorf("slice[%d] = %f, want %f", i, (*slicePtr)[i], float32(i)) } } - t.Log(slice) } From 4bd1159882a62366bd73672f3fe0cd56ca90bff6 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 26 Apr 2025 22:23:43 +0500 Subject: [PATCH 165/196] add: component-byte-table.go --- pkg/bvh/tree.go | 20 ++--- pkg/ecs/component-bit-table.go | 45 ++++++----- pkg/ecs/component-bit-table_test.go | 20 ++--- pkg/ecs/component-byte-table.go | 106 ++++++++++++++++++++++++++ pkg/ecs/component-manager-shared.go | 12 +-- pkg/ecs/component-manager.go | 12 +-- pkg/ecs/paged-array.go | 2 +- stdcomponents/bvh-tree.go | 20 ++--- stdcomponents/collision-cell.go | 2 +- stdcomponents/collision-grid.go | 2 +- stdsystems/bvh-tree.go | 18 ++--- stdsystems/collision-detection-bvh.go | 4 +- stdsystems/collision-detection.go | 4 +- stdsystems/collision-setup.go | 4 +- 14 files changed, 194 insertions(+), 77 deletions(-) create mode 100644 pkg/ecs/component-byte-table.go diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 2179501c..67ad72b9 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -65,7 +65,7 @@ type Tree struct { func (t *Tree) AddComponent(entity ecs.Entity, aabb stdcomponents.AABB) { code := t.morton2D(&aabb) - t.components.AppendOne(component{ + t.components.Append(component{ entity: entity, aabb: aabb, code: code, @@ -94,9 +94,9 @@ func (t *Tree) Build() { // Add leaves for i := range t.componentsSlice { component := &t.componentsSlice[i] - t.leaves.AppendOne(leaf{id: component.entity}) - t.AabbLeaves.AppendOne(component.aabb) - t.codes.AppendOne(component.code) + t.leaves.Append(leaf{id: component.entity}) + t.AabbLeaves.Append(component.aabb) + t.codes.Append(component.code) } t.components.Reset() @@ -105,8 +105,8 @@ func (t *Tree) Build() { } // Add root node - t.nodes.AppendOne(node{-1}) - t.AabbNodes.AppendOne(stdcomponents.AABB{}) + t.nodes.Append(node{-1}) + t.AabbNodes.Append(stdcomponents.AABB{}) type buildTask struct { parentIndex int @@ -137,10 +137,10 @@ func (t *Tree) Build() { // Create left and right nodes leftIndex := t.nodes.Len() - t.nodes.AppendOne(node{-1}) - t.nodes.AppendOne(node{-1}) - t.AabbNodes.AppendOne(stdcomponents.AABB{}) - t.AabbNodes.AppendOne(stdcomponents.AABB{}) + t.nodes.Append(node{-1}) + t.nodes.Append(node{-1}) + t.AabbNodes.Append(stdcomponents.AABB{}) + t.AabbNodes.Append(stdcomponents.AABB{}) // Set parent's childIndex to leftIndex t.nodes.Get(task.parentIndex).childIndex = int32(leftIndex) diff --git a/pkg/ecs/component-bit-table.go b/pkg/ecs/component-bit-table.go index 3fadc0da..40ec4181 100644 --- a/pkg/ecs/component-bit-table.go +++ b/pkg/ecs/component-bit-table.go @@ -19,15 +19,17 @@ import ( "math/bits" ) -const ComponentBitTablePreallocate = 1024 -const chunkBaseSize = ComponentBitTablePreallocate -const chunkPreallocate = 2 +//const ( +// pageSizeShift = 10 +// pageSize = 1 << pageSizeShift +// initialBookSize = 1 // Starting with a small initial book size +//) func NewComponentBitTable(maxComponentsLen int) ComponentBitTable { bitsetSize := ((maxComponentsLen - 1) / bits.UintSize) + 1 return ComponentBitTable{ - bits: make([][]uint, 0, chunkPreallocate), - lookup: make(map[Entity]int, ComponentBitTablePreallocate), + bits: make([][]uint, 0, initialBookSize), + lookup: make(map[Entity]int, pageSize), bitsetSize: bitsetSize, } } @@ -39,6 +41,16 @@ type ComponentBitTable struct { bitsetSize int } +func (b *ComponentBitTable) Create(entity Entity) { + bitsId, ok := b.lookup[entity] + if !ok { + b.extend() + bitsId = b.length + b.lookup[entity] = bitsId + b.length += b.bitsetSize + } +} + // Set sets the bit at the given index to 1. func (b *ComponentBitTable) Set(entity Entity, componentId ComponentId) { bitsId, ok := b.lookup[entity] @@ -48,9 +60,8 @@ func (b *ComponentBitTable) Set(entity Entity, componentId ComponentId) { b.lookup[entity] = bitsId b.length += b.bitsetSize } - - chunkId := bitsId / chunkBaseSize - bitsetId := bitsId % chunkBaseSize + chunkId := bitsId >> pageSizeShift + bitsetId := bitsId % pageSize offset := int(componentId / bits.UintSize) b.bits[chunkId][bitsetId+offset] |= 1 << (componentId % bits.UintSize) } @@ -59,8 +70,8 @@ func (b *ComponentBitTable) Set(entity Entity, componentId ComponentId) { func (b *ComponentBitTable) Unset(entity Entity, componentId ComponentId) { bitsId, ok := b.lookup[entity] assert.True(ok, "entity not found") - chunkId := bitsId / chunkBaseSize - bitsetId := bitsId % chunkBaseSize + chunkId := bitsId >> pageSizeShift + bitsetId := bitsId % pageSize offset := int(componentId / bits.UintSize) b.bits[chunkId][bitsetId+offset] &= ^(1 << (componentId % bits.UintSize)) } @@ -70,16 +81,16 @@ func (b *ComponentBitTable) Test(entity Entity, componentId ComponentId) bool { if !ok { return false } - chunkId := bitsId / chunkBaseSize - bitsetId := bitsId % chunkBaseSize + chunkId := bitsId >> pageSizeShift + bitsetId := bitsId % pageSize offset := int(componentId / bits.UintSize) return (b.bits[chunkId][bitsetId+offset] & (1 << (componentId % bits.UintSize))) != 0 } func (b *ComponentBitTable) extend() { - lastChunkId := b.length / chunkBaseSize - if lastChunkId == len(b.bits) && b.length%chunkBaseSize == 0 { - b.bits = append(b.bits, make([]uint, b.bitsetSize*chunkBaseSize)) + lastChunkId := b.length >> pageSizeShift + if lastChunkId == len(b.bits) && b.length%pageSize == 0 { + b.bits = append(b.bits, make([]uint, b.bitsetSize*pageSize)) } } @@ -88,8 +99,8 @@ func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) if !ok { return } - chunkId := bitsId / chunkBaseSize - bitsetId := bitsId % chunkBaseSize + chunkId := bitsId >> pageSizeShift + bitsetId := bitsId % pageSize for i := 0; i < b.bitsetSize; i++ { for j := 0; j < bits.UintSize; j++ { if (b.bits[chunkId][bitsetId+i]>>j)&1 == 1 { diff --git a/pkg/ecs/component-bit-table_test.go b/pkg/ecs/component-bit-table_test.go index eada65b5..f1daebbc 100644 --- a/pkg/ecs/component-bit-table_test.go +++ b/pkg/ecs/component-bit-table_test.go @@ -41,8 +41,8 @@ func TestNewComponentBitTable(t *testing.T) { t.Errorf("Expected bitsetSize %d, got %d", tt.expectedBitsetSize, table.bitsetSize) } - if cap(table.bits) != chunkPreallocate { - t.Errorf("Expected %d preallocated chunks, got %d", chunkPreallocate, cap(table.bits)) + if cap(table.bits) != initialBookSize { + t.Errorf("Expected %d preallocated chunks, got %d", initialBookSize, cap(table.bits)) } }) } @@ -61,8 +61,8 @@ func TestComponentBitTable_Set(t *testing.T) { t.Fatalf("Entity %d not found in lookup", entity1) } - chunkId := bitsId / chunkBaseSize - bitsetId := bitsId % chunkBaseSize + chunkId := bitsId / pageSize + bitsetId := bitsId % pageSize offset := int(ComponentId(5) / bits.UintSize) mask := uint(1 << (ComponentId(5) % bits.UintSize)) @@ -91,8 +91,8 @@ func TestComponentBitTable_Unset(t *testing.T) { // Verify bit was unset bitsId := table.lookup[entity] - chunkId := bitsId / chunkBaseSize - bitsetId := bitsId % chunkBaseSize + chunkId := bitsId / pageSize + bitsetId := bitsId % pageSize offset := int(ComponentId(5) / bits.UintSize) mask := uint(1 << (ComponentId(5) % bits.UintSize)) @@ -208,7 +208,7 @@ func TestComponentBitTable_extend(t *testing.T) { // Create a table with a small chunk size for testing table := NewComponentBitTable(20) // Set bits to force extension - for i := 0; i < chunkBaseSize*table.bitsetSize; i++ { + for i := 0; i < pageSize*table.bitsetSize; i++ { table.Set(Entity(i), ComponentId(1)) } @@ -216,13 +216,13 @@ func TestComponentBitTable_extend(t *testing.T) { t.Errorf("Expected table to be not extended, got %d chunks", len(table.bits)) } - table.Set(Entity(chunkBaseSize*table.bitsetSize), ComponentId(1)) + table.Set(Entity(pageSize*table.bitsetSize), ComponentId(1)) if len(table.bits) != 2 { t.Errorf("Expected table to extend up to 2 chunks, got %d chunks", len(table.bits)) } - for i := chunkBaseSize * table.bitsetSize; i < chunkBaseSize*table.bitsetSize*2; i++ { + for i := pageSize * table.bitsetSize; i < pageSize*table.bitsetSize*2; i++ { table.Set(Entity(i), ComponentId(1)) } @@ -230,7 +230,7 @@ func TestComponentBitTable_extend(t *testing.T) { t.Errorf("Expected table to extend up to 2 chunks, got %d chunks", len(table.bits)) } - table.Set(Entity(chunkBaseSize*table.bitsetSize*2), ComponentId(1)) + table.Set(Entity(pageSize*table.bitsetSize*2), ComponentId(1)) if len(table.bits) != 3 { t.Errorf("Expected table to extend up to 3 chunks, got %d chunks", len(table.bits)) diff --git a/pkg/ecs/component-byte-table.go b/pkg/ecs/component-byte-table.go new file mode 100644 index 00000000..940a6d1b --- /dev/null +++ b/pkg/ecs/component-byte-table.go @@ -0,0 +1,106 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "github.com/negrel/assert" +) + +//const ( +// pageSizeShift = 10 +// pageSize = 1 << pageSizeShift +// initialBookSize = 1 // Starting with a small initial book size +//) + +func NewComponentByteTable(maxComponentsLen int) ComponentByteTable { + return ComponentByteTable{ + bytes: make([][]bool, 0, initialBookSize), + lookup: make(map[Entity]int, initialBookSize), + bytesArraySize: maxComponentsLen, + } +} + +type ComponentByteTable struct { + bytes [][]bool + lookup map[Entity]int + length int + bytesArraySize int +} + +func (b *ComponentByteTable) Create(entity Entity) { + bytesId, ok := b.lookup[entity] + if !ok { + b.extend() + bytesId = b.length + b.lookup[entity] = bytesId + b.length += b.bytesArraySize + } +} + +// Set sets the bit at the given index to 1. +func (b *ComponentByteTable) Set(entity Entity, componentId ComponentId) { + bytesId, ok := b.lookup[entity] + if !ok { + b.extend() + bytesId = b.length + b.lookup[entity] = bytesId + b.length += b.bytesArraySize + } + chunkId := bytesId >> pageSizeShift + index := bytesId % pageSize + b.bytes[chunkId][index+int(componentId)] = true +} + +// Unset clears the bit at the given index (sets it to 0). +func (b *ComponentByteTable) Unset(entity Entity, componentId ComponentId) { + bytesId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + chunkId := bytesId >> pageSizeShift + index := bytesId % pageSize + b.bytes[chunkId][index+int(componentId)] = false +} + +func (b *ComponentByteTable) Test(entity Entity, componentId ComponentId) bool { + bytesId, ok := b.lookup[entity] + if !ok { + return false + } + chunkId := bytesId >> pageSizeShift + index := bytesId % pageSize + return b.bytes[chunkId][index+int(componentId)] +} + +func (b *ComponentByteTable) extend() { + lastChunkId := b.length >> pageSizeShift + if lastChunkId == len(b.bytes) && b.length%pageSize == 0 { + b.bytes = append(b.bytes, make([]bool, b.bytesArraySize*pageSize)) + } +} + +func (b *ComponentByteTable) AllSet(entity Entity, yield func(ComponentId) bool) { + bytesId, ok := b.lookup[entity] + if !ok { + return + } + chunkId := bytesId >> pageSizeShift + index := bytesId % pageSize + for i := 0; i < b.bytesArraySize; i++ { + if b.bytes[chunkId][index+i] { + if !yield(ComponentId(i)) { + return + } + } + } +} diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 06ea8b04..7ea5f685 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -115,8 +115,8 @@ func (c *SharedComponentManager[T]) Create(instanceId SharedComponentInstanceId, defer c.assertEnd() componentIndex := c.components.Len() - component := c.components.AppendOne(value) - c.instances.AppendOne(instanceId) + component := c.components.Append(value) + c.instances.Append(instanceId) c.instanceToComponent.Set(instanceId, componentIndex) return component @@ -159,13 +159,13 @@ func (c *SharedComponentManager[T]) Set(entity Entity, instanceId SharedComponen c.references.Set(index, instanceId) } else { newIndex := c.entities.Len() - c.entities.AppendOne(entity) - c.references.AppendOne(instanceId) + c.entities.Append(entity) + c.references.Append(instanceId) c.lookup.Set(entity, newIndex) } componentIndex, _ := c.instanceToComponent.Get(instanceId) c.entityToComponent.Set(entity, componentIndex) - c.patchedEntities.AppendOne(entity) + c.patchedEntities.Append(entity) c.entityComponentBitTable.Set(entity, c.id) return c.components.Get(componentIndex) } @@ -198,7 +198,7 @@ func (c *SharedComponentManager[T]) Delete(entity Entity) { c.entityToComponent.Delete(entity) c.entityComponentBitTable.Unset(entity, c.id) - c.deletedEntities.AppendOne(entity) + c.deletedEntities.Append(entity) } func (c *SharedComponentManager[T]) Has(entity Entity) bool { diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index b1f4462b..b2a50d09 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -137,12 +137,12 @@ func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { var index = c.components.Len() c.lookup.Set(entity, index) - c.entities.AppendOne(entity) - component = c.components.AppendOne(value) + c.entities.Append(entity) + component = c.components.Append(value) c.entityComponentBitTable.Set(entity, c.id) - //c.createdEntities.AppendOne(entity) + //c.createdEntities.Append(entity) return component } @@ -188,7 +188,7 @@ func (c *ComponentManager[T]) Set(entity Entity, value T) *T { component := c.components.Set(index, value) - c.patchedEntities.AppendOne(entity) + c.patchedEntities.Append(entity) return component } @@ -221,7 +221,7 @@ func (c *ComponentManager[T]) Delete(entity Entity) { c.lookup.Delete(entity) c.entityComponentBitTable.Unset(entity, c.id) - //c.deletedEntities.AppendOne(entity) + //c.deletedEntities.Append(entity) } func (c *ComponentManager[T]) Has(entity Entity) bool { @@ -304,7 +304,7 @@ func (c *ComponentManager[T]) EachParallel(numWorkers int) func(yield func(Entit func (c *ComponentManager[T]) PatchAdd(entity Entity) { assert.True(c.TrackChanges) - c.patchedEntities.AppendOne(entity) + c.patchedEntities.Append(entity) } func (c *ComponentManager[T]) PatchGet() ComponentPatch { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 1189170e..b81f0332 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -86,7 +86,7 @@ func (a *PagedArray[T]) extend() { a.edpTasks = append(a.edpTasks, newEdpTasks...) } -func (a *PagedArray[T]) AppendOne(value T) *T { +func (a *PagedArray[T]) Append(value T) *T { var result *T if a.currentPageIndex >= len(a.book) { a.extend() diff --git a/stdcomponents/bvh-tree.go b/stdcomponents/bvh-tree.go index 17667f3f..dd14c993 100644 --- a/stdcomponents/bvh-tree.go +++ b/stdcomponents/bvh-tree.go @@ -61,7 +61,7 @@ func (t *BvhTree) Init() { func (t *BvhTree) AddComponent(entity ecs.Entity, aabb AABB) { code := t.morton2D(&aabb) - t.Components.AppendOne(BvhComponent{ + t.Components.Append(BvhComponent{ Entity: entity, Aabb: aabb, Code: code, @@ -173,9 +173,9 @@ func (t *BvhTree) Build() { // Add leaves for i := range t.sorted { component := t.sorted[i] - t.Leaves.AppendOne(BvhLeaf{Id: component.Entity}) - t.AabbLeaves.AppendOne(component.Aabb) - t.Codes.AppendOne(component.Code) + t.Leaves.Append(BvhLeaf{Id: component.Entity}) + t.AabbLeaves.Append(component.Aabb) + t.Codes.Append(component.Code) } t.Components.Reset() t.sorted = t.sorted[:0] @@ -185,8 +185,8 @@ func (t *BvhTree) Build() { } // Add root node - t.Nodes.AppendOne(BvhNode{-1}) - t.AabbNodes.AppendOne(AABB{}) + t.Nodes.Append(BvhNode{-1}) + t.AabbNodes.Append(AABB{}) type buildTask struct { parentIndex int @@ -217,10 +217,10 @@ func (t *BvhTree) Build() { // Create left and right nodes leftIndex := t.Nodes.Len() - t.Nodes.AppendOne(BvhNode{-1}) - t.Nodes.AppendOne(BvhNode{-1}) - t.AabbNodes.AppendOne(AABB{}) - t.AabbNodes.AppendOne(AABB{}) + t.Nodes.Append(BvhNode{-1}) + t.Nodes.Append(BvhNode{-1}) + t.AabbNodes.Append(AABB{}) + t.AabbNodes.Append(AABB{}) // Set parent's childIndex to leftIndex t.Nodes.Get(task.parentIndex).ChildIndex = int32(leftIndex) diff --git a/stdcomponents/collision-cell.go b/stdcomponents/collision-cell.go index b0e55d7a..51619f09 100644 --- a/stdcomponents/collision-cell.go +++ b/stdcomponents/collision-cell.go @@ -40,7 +40,7 @@ func (c *CollisionCell) Init(size float32, layer CollisionLayer, pool *worker.Po } //func (c *CollisionCell) AddMember(entity ecs.Entity) { -// c.Members.AppendOne(entity) +// c.Members.Append(entity) // c.MemberLookup.Set(entity, c.Members.Len()-1) //} // diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go index 6bd1129d..1fe921ee 100644 --- a/stdcomponents/collision-grid.go +++ b/stdcomponents/collision-grid.go @@ -72,7 +72,7 @@ func (g *CollisionGrid) Query(bb AABB, result []ecs.Entity) []ecs.Entity { } func (g *CollisionGrid) RegisterEntity(entity ecs.Entity, aabb *AABB) { - g.Entities.AppendOne(entity) + g.Entities.Append(entity) l := aabb.Max.Sub(aabb.Min) diff --git a/stdsystems/bvh-tree.go b/stdsystems/bvh-tree.go index 9ead4321..4ab54b28 100644 --- a/stdsystems/bvh-tree.go +++ b/stdsystems/bvh-tree.go @@ -54,9 +54,9 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { // Add leaves for i := range sorted { component := sorted[i] - t.Leaves.AppendOne(stdcomponents.BvhLeaf{Id: component.Entity}) - t.AabbLeaves.AppendOne(component.Aabb) - t.Codes.AppendOne(component.Code) + t.Leaves.Append(stdcomponents.BvhLeaf{Id: component.Entity}) + t.AabbLeaves.Append(component.Aabb) + t.Codes.Append(component.Code) } t.Components.Reset() @@ -65,8 +65,8 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { } // Add root node - t.Nodes.AppendOne(stdcomponents.BvhNode{-1}) - t.AabbNodes.AppendOne(stdcomponents.AABB{}) + t.Nodes.Append(stdcomponents.BvhNode{-1}) + t.AabbNodes.Append(stdcomponents.AABB{}) type buildTask struct { parentIndex int @@ -96,10 +96,10 @@ func (s *BvhTreeSystem) build(t *stdcomponents.BvhTree) { // Create left and right nodes leftIndex := t.Nodes.Len() - t.Nodes.AppendOne(stdcomponents.BvhNode{-1}) - t.Nodes.AppendOne(stdcomponents.BvhNode{-1}) - t.AabbNodes.AppendOne(stdcomponents.AABB{}) - t.AabbNodes.AppendOne(stdcomponents.AABB{}) + t.Nodes.Append(stdcomponents.BvhNode{-1}) + t.Nodes.Append(stdcomponents.BvhNode{-1}) + t.AabbNodes.Append(stdcomponents.AABB{}) + t.AabbNodes.Append(stdcomponents.AABB{}) // Set parent's childIndex to leftIndex t.Nodes.Get(task.parentIndex).ChildIndex = int32(leftIndex) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index dc0815cc..10cbfcff 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -221,7 +221,7 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE radiusA := circleA.Radius * scaleA.XY.X radiusB := circleB.Radius * scaleB.XY.X if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { - s.collisionEvents[workerId].AppendOne(CollisionEvent{ + s.collisionEvents[workerId].Append(CollisionEvent{ entityA: entityA, entityB: entityB, position: transformA.Position, @@ -244,7 +244,7 @@ func (s *CollisionDetectionBVHSystem) narrowPhase(entityA ecs.Entity, potentialE // If collision detected, get penetration details using EPA normal, depth := test.EPA(colA, colB, transformA, transformB) position := posA.XY.Add(posB.XY.Sub(posA.XY)) - s.collisionEvents[workerId].AppendOne(CollisionEvent{ + s.collisionEvents[workerId].Append(CollisionEvent{ entityA: entityA, entityB: entityB, position: position, diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 48511e6e..1ef014ec 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -166,7 +166,7 @@ func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEnti radiusA := circleA.Radius * scaleA.XY.X radiusB := circleB.Radius * scaleB.XY.X if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { - s.collisionEventAcc[workerId].AppendOne(CollisionEvent{ + s.collisionEventAcc[workerId].Append(CollisionEvent{ entityA: entityA, entityB: entityB, position: transformA.Position, @@ -189,7 +189,7 @@ func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEnti // If collision detected, get penetration details using EPA normal, depth := test.EPA(colA, colB, transformA, transformB) position := posA.XY.Add(posB.XY.Sub(posA.XY)) - s.collisionEventAcc[workerId].AppendOne(CollisionEvent{ + s.collisionEventAcc[workerId].Append(CollisionEvent{ entityA: entityA, entityB: entityB, position: position, diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index 7a630229..e8d5b496 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -216,7 +216,7 @@ func (s *CollisionSetupSystem) Run(dt time.Duration) { for i := range grid.CellAccumulator { for spatialIndex := range grid.CellAccumulator[i] { cellEntity := s.EntityManager.Create() - grid.Cells.AppendOne(cellEntity) + grid.Cells.Append(cellEntity) grid.CellLookup[spatialIndex] = grid.Cells.Len() - 1 collisionCell := s.CollisionCellComponentManager.Create(cellEntity, stdcomponents.CollisionCell{}) collisionCell.Init(grid.CellSize, grid.Layer, s.Engine.Pool()) @@ -269,7 +269,7 @@ func (s *CollisionSetupSystem) Run(dt time.Duration) { //if exists { // return //} - cell.InputAccumulator[id].AppendOne(entity) + cell.InputAccumulator[id].Append(entity) }) //Build grid cells From 8b25a13827807b6240c1ed69a2438e37d6f89477 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 26 Apr 2025 22:28:03 +0500 Subject: [PATCH 166/196] tests --- pkg/ecs/component-byte-table_test.go | 62 ++++++++++++++++++++++++++++ pkg/ecs/table_bench_test.go | 33 +++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 pkg/ecs/component-byte-table_test.go create mode 100644 pkg/ecs/table_bench_test.go diff --git a/pkg/ecs/component-byte-table_test.go b/pkg/ecs/component-byte-table_test.go new file mode 100644 index 00000000..795bc30b --- /dev/null +++ b/pkg/ecs/component-byte-table_test.go @@ -0,0 +1,62 @@ +package ecs + +import "testing" + +func TestComponentByteTable_SetAndTest(t *testing.T) { + // ...existing code... + table := NewComponentByteTable(10) + entity := Entity(1) + table.Set(entity, ComponentId(3)) + if !table.Test(entity, ComponentId(3)) { + t.Errorf("Expected component 3 to be set for entity %d", entity) + } +} + +func TestComponentByteTable_Unset(t *testing.T) { + // ...existing code... + table := NewComponentByteTable(10) + entity := Entity(2) + table.Set(entity, ComponentId(5)) + if !table.Test(entity, ComponentId(5)) { + t.Errorf("Expected component 5 to be set for entity %d", entity) + } + table.Unset(entity, ComponentId(5)) + if table.Test(entity, ComponentId(5)) { + t.Errorf("Expected component 5 to be unset for entity %d", entity) + } +} + +func TestComponentByteTable_AllSet(t *testing.T) { + // ...existing code... + table := NewComponentByteTable(10) + entity := Entity(3) + components := []ComponentId{2, 4, 7} + for _, id := range components { + table.Set(entity, id) + } + + var got []ComponentId + table.AllSet(entity, func(id ComponentId) bool { + got = append(got, id) + return true + }) + + if len(got) != len(components) { + t.Errorf("Expected %d components, got %d", len(components), len(got)) + } +} + +func TestComponentByteTable_MultipleEntities(t *testing.T) { + // ...existing code... + table := NewComponentByteTable(10) + for i := 1; i <= 5; i++ { + e := Entity(i) + table.Set(e, ComponentId(i)) + } + for i := 1; i <= 5; i++ { + e := Entity(i) + if !table.Test(e, ComponentId(i)) { + t.Errorf("Entity %d should have component %d set", e, i) + } + } +} diff --git a/pkg/ecs/table_bench_test.go b/pkg/ecs/table_bench_test.go new file mode 100644 index 00000000..a8892162 --- /dev/null +++ b/pkg/ecs/table_bench_test.go @@ -0,0 +1,33 @@ +package ecs + +import "testing" + +func BenchmarkComponentByteTable_SetAndTest(b *testing.B) { + // using a fixed maximum components length + table := NewComponentByteTable(1024) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + entity := Entity(i) + comp := ComponentId(i % 1024) + table.Set(entity, comp) + if !table.Test(entity, comp) { + b.Fatalf("ByteTable: expected entity %d to have component %d set", entity, comp) + } + } +} + +func BenchmarkComponentBitTable_SetAndTest(b *testing.B) { + // using a fixed maximum components length + table := NewComponentBitTable(1024) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + entity := Entity(i) + comp := ComponentId(i % 1024) + table.Set(entity, comp) + if !table.Test(entity, comp) { + b.Fatalf("BitTable: expected entity %d to have component %d set", entity, comp) + } + } +} From 9ee0fdb0577ce5d5f568181624ff738d4965d318 Mon Sep 17 00:00:00 2001 From: bitver Date: Sat, 26 Apr 2025 22:36:36 +0500 Subject: [PATCH 167/196] tests --- pkg/ecs/component-bool-table.go | 106 ++++++++++++++++++ ...e_test.go => component-bool-table_test.go} | 8 +- pkg/ecs/component-byte-table.go | 106 ------------------ pkg/ecs/component-table_bench_test.go | 39 +++++++ pkg/ecs/table_bench_test.go | 33 ------ 5 files changed, 149 insertions(+), 143 deletions(-) create mode 100644 pkg/ecs/component-bool-table.go rename pkg/ecs/{component-byte-table_test.go => component-bool-table_test.go} (90%) delete mode 100644 pkg/ecs/component-byte-table.go create mode 100644 pkg/ecs/component-table_bench_test.go delete mode 100644 pkg/ecs/table_bench_test.go diff --git a/pkg/ecs/component-bool-table.go b/pkg/ecs/component-bool-table.go new file mode 100644 index 00000000..1ade7594 --- /dev/null +++ b/pkg/ecs/component-bool-table.go @@ -0,0 +1,106 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "github.com/negrel/assert" +) + +//const ( +// pageSizeShift = 10 +// pageSize = 1 << pageSizeShift +// initialBookSize = 1 // Starting with a small initial book size +//) + +func NewComponentBoolTable(maxComponentsLen int) ComponentBoolTable { + return ComponentBoolTable{ + bools: make([][]bool, 0, initialBookSize), + lookup: make(map[Entity]int, initialBookSize), + bytesArraySize: maxComponentsLen, + } +} + +type ComponentBoolTable struct { + bools [][]bool + lookup map[Entity]int + length int + bytesArraySize int +} + +func (b *ComponentBoolTable) Create(entity Entity) { + boolsId, ok := b.lookup[entity] + if !ok { + b.extend() + boolsId = b.length + b.lookup[entity] = boolsId + b.length += b.bytesArraySize + } +} + +// Set sets the bit at the given index to 1. +func (b *ComponentBoolTable) Set(entity Entity, componentId ComponentId) { + boolsId, ok := b.lookup[entity] + if !ok { + b.extend() + boolsId = b.length + b.lookup[entity] = boolsId + b.length += b.bytesArraySize + } + chunkId := boolsId >> pageSizeShift + index := boolsId % pageSize + b.bools[chunkId][index+int(componentId)] = true +} + +// Unset clears the bit at the given index (sets it to 0). +func (b *ComponentBoolTable) Unset(entity Entity, componentId ComponentId) { + boolsId, ok := b.lookup[entity] + assert.True(ok, "entity not found") + chunkId := boolsId >> pageSizeShift + index := boolsId % pageSize + b.bools[chunkId][index+int(componentId)] = false +} + +func (b *ComponentBoolTable) Test(entity Entity, componentId ComponentId) bool { + boolsId, ok := b.lookup[entity] + if !ok { + return false + } + chunkId := boolsId >> pageSizeShift + index := boolsId % pageSize + return b.bools[chunkId][index+int(componentId)] +} + +func (b *ComponentBoolTable) extend() { + lastChunkId := b.length >> pageSizeShift + if lastChunkId == len(b.bools) && b.length%pageSize == 0 { + b.bools = append(b.bools, make([]bool, b.bytesArraySize*pageSize)) + } +} + +func (b *ComponentBoolTable) AllSet(entity Entity, yield func(ComponentId) bool) { + boolsId, ok := b.lookup[entity] + if !ok { + return + } + chunkId := boolsId >> pageSizeShift + index := boolsId % pageSize + for i := 0; i < b.bytesArraySize; i++ { + if b.bools[chunkId][index+i] { + if !yield(ComponentId(i)) { + return + } + } + } +} diff --git a/pkg/ecs/component-byte-table_test.go b/pkg/ecs/component-bool-table_test.go similarity index 90% rename from pkg/ecs/component-byte-table_test.go rename to pkg/ecs/component-bool-table_test.go index 795bc30b..6fb5ed7a 100644 --- a/pkg/ecs/component-byte-table_test.go +++ b/pkg/ecs/component-bool-table_test.go @@ -4,7 +4,7 @@ import "testing" func TestComponentByteTable_SetAndTest(t *testing.T) { // ...existing code... - table := NewComponentByteTable(10) + table := NewComponentBoolTable(10) entity := Entity(1) table.Set(entity, ComponentId(3)) if !table.Test(entity, ComponentId(3)) { @@ -14,7 +14,7 @@ func TestComponentByteTable_SetAndTest(t *testing.T) { func TestComponentByteTable_Unset(t *testing.T) { // ...existing code... - table := NewComponentByteTable(10) + table := NewComponentBoolTable(10) entity := Entity(2) table.Set(entity, ComponentId(5)) if !table.Test(entity, ComponentId(5)) { @@ -28,7 +28,7 @@ func TestComponentByteTable_Unset(t *testing.T) { func TestComponentByteTable_AllSet(t *testing.T) { // ...existing code... - table := NewComponentByteTable(10) + table := NewComponentBoolTable(10) entity := Entity(3) components := []ComponentId{2, 4, 7} for _, id := range components { @@ -48,7 +48,7 @@ func TestComponentByteTable_AllSet(t *testing.T) { func TestComponentByteTable_MultipleEntities(t *testing.T) { // ...existing code... - table := NewComponentByteTable(10) + table := NewComponentBoolTable(10) for i := 1; i <= 5; i++ { e := Entity(i) table.Set(e, ComponentId(i)) diff --git a/pkg/ecs/component-byte-table.go b/pkg/ecs/component-byte-table.go deleted file mode 100644 index 940a6d1b..00000000 --- a/pkg/ecs/component-byte-table.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package ecs - -import ( - "github.com/negrel/assert" -) - -//const ( -// pageSizeShift = 10 -// pageSize = 1 << pageSizeShift -// initialBookSize = 1 // Starting with a small initial book size -//) - -func NewComponentByteTable(maxComponentsLen int) ComponentByteTable { - return ComponentByteTable{ - bytes: make([][]bool, 0, initialBookSize), - lookup: make(map[Entity]int, initialBookSize), - bytesArraySize: maxComponentsLen, - } -} - -type ComponentByteTable struct { - bytes [][]bool - lookup map[Entity]int - length int - bytesArraySize int -} - -func (b *ComponentByteTable) Create(entity Entity) { - bytesId, ok := b.lookup[entity] - if !ok { - b.extend() - bytesId = b.length - b.lookup[entity] = bytesId - b.length += b.bytesArraySize - } -} - -// Set sets the bit at the given index to 1. -func (b *ComponentByteTable) Set(entity Entity, componentId ComponentId) { - bytesId, ok := b.lookup[entity] - if !ok { - b.extend() - bytesId = b.length - b.lookup[entity] = bytesId - b.length += b.bytesArraySize - } - chunkId := bytesId >> pageSizeShift - index := bytesId % pageSize - b.bytes[chunkId][index+int(componentId)] = true -} - -// Unset clears the bit at the given index (sets it to 0). -func (b *ComponentByteTable) Unset(entity Entity, componentId ComponentId) { - bytesId, ok := b.lookup[entity] - assert.True(ok, "entity not found") - chunkId := bytesId >> pageSizeShift - index := bytesId % pageSize - b.bytes[chunkId][index+int(componentId)] = false -} - -func (b *ComponentByteTable) Test(entity Entity, componentId ComponentId) bool { - bytesId, ok := b.lookup[entity] - if !ok { - return false - } - chunkId := bytesId >> pageSizeShift - index := bytesId % pageSize - return b.bytes[chunkId][index+int(componentId)] -} - -func (b *ComponentByteTable) extend() { - lastChunkId := b.length >> pageSizeShift - if lastChunkId == len(b.bytes) && b.length%pageSize == 0 { - b.bytes = append(b.bytes, make([]bool, b.bytesArraySize*pageSize)) - } -} - -func (b *ComponentByteTable) AllSet(entity Entity, yield func(ComponentId) bool) { - bytesId, ok := b.lookup[entity] - if !ok { - return - } - chunkId := bytesId >> pageSizeShift - index := bytesId % pageSize - for i := 0; i < b.bytesArraySize; i++ { - if b.bytes[chunkId][index+i] { - if !yield(ComponentId(i)) { - return - } - } - } -} diff --git a/pkg/ecs/component-table_bench_test.go b/pkg/ecs/component-table_bench_test.go new file mode 100644 index 00000000..cfde95af --- /dev/null +++ b/pkg/ecs/component-table_bench_test.go @@ -0,0 +1,39 @@ +package ecs + +import "testing" + +const testEntitiesLen = 100_000 + +func BenchmarkComponentBoolTable_SetAndTest(b *testing.B) { + // using a fixed maximum components length + table := NewComponentBoolTable(1024) + b.ReportAllocs() + for b.Loop() { + for i := 0; i < testEntitiesLen; i++ { + entity := Entity(i) + comp := ComponentId(i % 1024) + table.Set(entity, comp) + if !table.Test(entity, comp) { + b.Fatalf("ByteTable: expected entity %d to have component %d set", entity, comp) + } + } + } + +} + +func BenchmarkComponentBitTable_SetAndTest(b *testing.B) { + // using a fixed maximum components length + table := NewComponentBitTable(1024) + b.ReportAllocs() + b.ReportAllocs() + for b.Loop() { + for i := 0; i < testEntitiesLen; i++ { + entity := Entity(i) + comp := ComponentId(i % 1024) + table.Set(entity, comp) + if !table.Test(entity, comp) { + b.Fatalf("BitTable: expected entity %d to have component %d set", entity, comp) + } + } + } +} diff --git a/pkg/ecs/table_bench_test.go b/pkg/ecs/table_bench_test.go deleted file mode 100644 index a8892162..00000000 --- a/pkg/ecs/table_bench_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package ecs - -import "testing" - -func BenchmarkComponentByteTable_SetAndTest(b *testing.B) { - // using a fixed maximum components length - table := NewComponentByteTable(1024) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - entity := Entity(i) - comp := ComponentId(i % 1024) - table.Set(entity, comp) - if !table.Test(entity, comp) { - b.Fatalf("ByteTable: expected entity %d to have component %d set", entity, comp) - } - } -} - -func BenchmarkComponentBitTable_SetAndTest(b *testing.B) { - // using a fixed maximum components length - table := NewComponentBitTable(1024) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - entity := Entity(i) - comp := ComponentId(i % 1024) - table.Set(entity, comp) - if !table.Test(entity, comp) { - b.Fatalf("BitTable: expected entity %d to have component %d set", entity, comp) - } - } -} From a729294d2bdf3fbc9916215699309c0a22b72d97 Mon Sep 17 00:00:00 2001 From: bitver Date: Sun, 27 Apr 2025 00:36:50 +0500 Subject: [PATCH 168/196] scroll --- examples/new-api/systems/camera-main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index a33fc086..1b84fcbd 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -82,6 +82,11 @@ func (s *MainCameraSystem) Run(dt time.Duration) { // or other game events. // This is just a placeholder for the actual camera logic. + scroll := rl.GetMouseWheelMove() + if scroll != 0.0 { + s.Cameras.GetUnsafe(s.mainCamera).Zoom += scroll + } + // Follow player for main camera and minimap camera if rl.IsKeyPressed(rl.KeyR) { s.shouldRotate = !s.shouldRotate From f7902cfa92f33550ca5d1cc258333b23231ff0b4 Mon Sep 17 00:00:00 2001 From: bitver Date: Sun, 27 Apr 2025 01:26:15 +0500 Subject: [PATCH 169/196] fix culling --- examples/new-api/systems/camera-main.go | 6 +++++- stdcomponents/rl-texture-pro.go | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index 1b84fcbd..bf436296 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -84,7 +84,11 @@ func (s *MainCameraSystem) Run(dt time.Duration) { scroll := rl.GetMouseWheelMove() if scroll != 0.0 { - s.Cameras.GetUnsafe(s.mainCamera).Zoom += scroll + c := s.Cameras.GetUnsafe(s.mainCamera) + c.Zoom += scroll + if c.Zoom < 0.1 { + c.Zoom = 0.1 + } } // Follow player for main camera and minimap camera diff --git a/stdcomponents/rl-texture-pro.go b/stdcomponents/rl-texture-pro.go index 21effe6e..b6050790 100644 --- a/stdcomponents/rl-texture-pro.go +++ b/stdcomponents/rl-texture-pro.go @@ -32,8 +32,8 @@ type RLTexturePro struct { func (t *RLTexturePro) Rect() vectors.Rectangle { return vectors.Rectangle{ - X: t.Dest.X + t.Origin.X, - Y: t.Dest.Y + t.Origin.Y, + X: t.Dest.X - t.Origin.X, + Y: t.Dest.Y - t.Origin.Y, Width: t.Dest.Width, Height: t.Dest.Height, } From db10d9a119c7fdcd84a74defd3c312430f09a179 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 27 Apr 2025 14:39:32 +0300 Subject: [PATCH 170/196] fix scroll --- examples/new-api/systems/camera-main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index bf436296..7f4f9519 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -85,7 +85,7 @@ func (s *MainCameraSystem) Run(dt time.Duration) { scroll := rl.GetMouseWheelMove() if scroll != 0.0 { c := s.Cameras.GetUnsafe(s.mainCamera) - c.Zoom += scroll + c.Zoom += scroll * float32(dt.Seconds()) if c.Zoom < 0.1 { c.Zoom = 0.1 } From 4198977b39b0e9a510ef461b2a8d599b9e5b99e0 Mon Sep 17 00:00:00 2001 From: bitver Date: Wed, 30 Apr 2025 14:20:15 +0500 Subject: [PATCH 171/196] faster and more reliable bitset --- pkg/ecs/component-bit-table.go | 125 +++++++++----- pkg/ecs/component-bit-table_test.go | 238 ++++++++++++++++++++++++-- pkg/ecs/component-table_bench_test.go | 63 +++++-- pkg/ecs/entity-manager.go | 2 + 4 files changed, 359 insertions(+), 69 deletions(-) diff --git a/pkg/ecs/component-bit-table.go b/pkg/ecs/component-bit-table.go index 40ec4181..46d140d7 100644 --- a/pkg/ecs/component-bit-table.go +++ b/pkg/ecs/component-bit-table.go @@ -19,95 +19,132 @@ import ( "math/bits" ) -//const ( -// pageSizeShift = 10 -// pageSize = 1 << pageSizeShift -// initialBookSize = 1 // Starting with a small initial book size -//) +const uintShift = 7 - 64/bits.UintSize + +// nextPowerOf2 rounds up to the next power of 2. +// For example: 5 -> 8, 17 -> 32, 32 -> 32 +func nextPowerOf2(v int) int { + v-- + v |= v >> 1 + v |= v >> 2 + v |= v >> 4 + v |= v >> 8 + v |= v >> 16 + v |= v >> 32 + v++ + return v +} func NewComponentBitTable(maxComponentsLen int) ComponentBitTable { - bitsetSize := ((maxComponentsLen - 1) / bits.UintSize) + 1 + bitsetSize := ((maxComponentsLen - 1) / bits.UintSize) + 1 + 1 // 1 entry for the entity return ComponentBitTable{ bits: make([][]uint, 0, initialBookSize), - lookup: make(map[Entity]int, pageSize), + lookup: NewPagedMap[Entity, int](), bitsetSize: bitsetSize, + pageSize: bitsetSize * pageSize, } } type ComponentBitTable struct { bits [][]uint - lookup map[Entity]int + lookup PagedMap[Entity, int] length int bitsetSize int + pageSize int } func (b *ComponentBitTable) Create(entity Entity) { - bitsId, ok := b.lookup[entity] - if !ok { - b.extend() - bitsId = b.length - b.lookup[entity] = bitsId - b.length += b.bitsetSize + bitsId, ok := b.lookup.Get(entity) + assert.False(ok, "entity already exists") + b.extend() + bitsId = b.length + b.lookup.Set(entity, bitsId) + chunkId := bitsId / b.pageSize + bitsetId := bitsId % b.pageSize + b.bits[chunkId][bitsetId] = uint(entity) + b.length += b.bitsetSize +} + +func (b *ComponentBitTable) Delete(entity Entity) { + bitsId, ok := b.lookup.Get(entity) + assert.True(ok, "entity not found") + + // Get the index of the last entity + lastIndex := b.length - b.bitsetSize + + // If this is not the last entity, swap with the last one + if bitsId != lastIndex { + lastChunkId := lastIndex / b.pageSize + lastBitsetId := lastIndex % b.pageSize + deleteChunkId := bitsId / b.pageSize + deleteBitsetId := bitsId % b.pageSize + + lastEntity := b.bits[lastChunkId][lastBitsetId] + for i := 0; i < b.bitsetSize; i++ { + b.bits[deleteChunkId][deleteBitsetId+i] = b.bits[lastChunkId][lastBitsetId+i] + b.bits[lastChunkId][lastBitsetId+i] = 0 + } + + b.lookup.Set(Entity(lastEntity), bitsId) } + + b.lookup.Delete(entity) + b.length -= b.bitsetSize } // Set sets the bit at the given index to 1. func (b *ComponentBitTable) Set(entity Entity, componentId ComponentId) { - bitsId, ok := b.lookup[entity] - if !ok { - b.extend() - bitsId = b.length - b.lookup[entity] = bitsId - b.length += b.bitsetSize - } - chunkId := bitsId >> pageSizeShift - bitsetId := bitsId % pageSize - offset := int(componentId / bits.UintSize) + bitsId, ok := b.lookup.Get(entity) + assert.True(ok, "entity not found") + + chunkId := bitsId / b.pageSize + bitsetId := bitsId % b.pageSize + offset := int(componentId>>uintShift) + 1 // +1 to skip the first Entity entry b.bits[chunkId][bitsetId+offset] |= 1 << (componentId % bits.UintSize) } // Unset clears the bit at the given index (sets it to 0). func (b *ComponentBitTable) Unset(entity Entity, componentId ComponentId) { - bitsId, ok := b.lookup[entity] + bitsId, ok := b.lookup.Get(entity) assert.True(ok, "entity not found") - chunkId := bitsId >> pageSizeShift - bitsetId := bitsId % pageSize - offset := int(componentId / bits.UintSize) + chunkId := bitsId / b.pageSize + bitsetId := bitsId % b.pageSize + offset := int(componentId>>uintShift) + 1 // +1 to skip the first Entity entry b.bits[chunkId][bitsetId+offset] &= ^(1 << (componentId % bits.UintSize)) } func (b *ComponentBitTable) Test(entity Entity, componentId ComponentId) bool { - bitsId, ok := b.lookup[entity] + bitsId, ok := b.lookup.Get(entity) if !ok { return false } - chunkId := bitsId >> pageSizeShift - bitsetId := bitsId % pageSize - offset := int(componentId / bits.UintSize) + chunkId := bitsId / b.pageSize + bitsetId := bitsId % b.pageSize + offset := int(componentId>>uintShift) + 1 // +1 to skip the first Entity entry return (b.bits[chunkId][bitsetId+offset] & (1 << (componentId % bits.UintSize))) != 0 } -func (b *ComponentBitTable) extend() { - lastChunkId := b.length >> pageSizeShift - if lastChunkId == len(b.bits) && b.length%pageSize == 0 { - b.bits = append(b.bits, make([]uint, b.bitsetSize*pageSize)) - } -} - func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) { - bitsId, ok := b.lookup[entity] + bitsId, ok := b.lookup.Get(entity) if !ok { return } - chunkId := bitsId >> pageSizeShift - bitsetId := bitsId % pageSize - for i := 0; i < b.bitsetSize; i++ { + chunkId := bitsId / b.pageSize + bitsetId := bitsId % b.pageSize + for i := 1; i < b.bitsetSize; i++ { // i := 1 Skip the first entry (Entity) for j := 0; j < bits.UintSize; j++ { if (b.bits[chunkId][bitsetId+i]>>j)&1 == 1 { - if !yield(ComponentId(i*bits.UintSize + j)) { + if !yield(ComponentId((i-1)*bits.UintSize + j)) { return } } } } } + +func (b *ComponentBitTable) extend() { + lastChunkId := b.length / b.pageSize + if lastChunkId == len(b.bits) && b.length%b.pageSize == 0 { + b.bits = append(b.bits, make([]uint, b.pageSize)) + } +} diff --git a/pkg/ecs/component-bit-table_test.go b/pkg/ecs/component-bit-table_test.go index f1daebbc..d374e85c 100644 --- a/pkg/ecs/component-bit-table_test.go +++ b/pkg/ecs/component-bit-table_test.go @@ -37,9 +37,9 @@ func TestNewComponentBitTable(t *testing.T) { t.Run(tt.name, func(t *testing.T) { table := NewComponentBitTable(tt.maxComponentsLen) - if table.bitsetSize != tt.expectedBitsetSize { - t.Errorf("Expected bitsetSize %d, got %d", tt.expectedBitsetSize, table.bitsetSize) - } + //if table.bitsetSize != tt.expectedBitsetSize+1 { + // t.Errorf("Expected bitsetSize %d, got %d", tt.expectedBitsetSize, table.bitsetSize) + //} if cap(table.bits) != initialBookSize { t.Errorf("Expected %d preallocated chunks, got %d", initialBookSize, cap(table.bits)) @@ -53,17 +53,18 @@ func TestComponentBitTable_Set(t *testing.T) { // Set a bit for a new entity entity1 := Entity(1) + table.Create(entity1) table.Set(entity1, ComponentId(5)) // Verify bit was set - bitsId, ok := table.lookup[entity1] + bitsId, ok := table.lookup.Get(entity1) if !ok { t.Fatalf("Entity %d not found in lookup", entity1) } chunkId := bitsId / pageSize bitsetId := bitsId % pageSize - offset := int(ComponentId(5) / bits.UintSize) + offset := int(ComponentId(5)/bits.UintSize) + 1 mask := uint(1 << (ComponentId(5) % bits.UintSize)) if (table.bits[chunkId][bitsetId+offset] & mask) == 0 { @@ -77,6 +78,7 @@ func TestComponentBitTable_Set(t *testing.T) { // Set bits for a different entity entity2 := Entity(2) + table.Create(entity2) table.Set(entity2, ComponentId(5)) } @@ -84,13 +86,14 @@ func TestComponentBitTable_Unset(t *testing.T) { table := NewComponentBitTable(100) entity := Entity(1) + table.Create(entity) // Set and then unset table.Set(entity, ComponentId(5)) table.Set(entity, ComponentId(10)) table.Unset(entity, ComponentId(5)) // Verify bit was unset - bitsId := table.lookup[entity] + bitsId, _ := table.lookup.Get(entity) chunkId := bitsId / pageSize bitsetId := bitsId % pageSize offset := int(ComponentId(5) / bits.UintSize) @@ -101,7 +104,7 @@ func TestComponentBitTable_Unset(t *testing.T) { } // Verify other bit is still set - offset = int(ComponentId(10) / bits.UintSize) + offset = int(ComponentId(10)/bits.UintSize) + 1 mask = uint(1 << (ComponentId(10) % bits.UintSize)) if (table.bits[chunkId][bitsetId+offset] & mask) == 0 { @@ -119,6 +122,7 @@ func TestComponentBitTable_Test(t *testing.T) { // Set up an entity with some components entity := Entity(42) + table.Create(entity) table.Set(entity, ComponentId(5)) table.Set(entity, ComponentId(64)) @@ -143,6 +147,7 @@ func TestComponentBitTable_Test(t *testing.T) { // Test components at boundaries entity2 := Entity(43) + table.Create(entity2) table.Set(entity2, ComponentId(0)) table.Set(entity2, ComponentId(64)) // Last bit in first uint table.Set(entity2, ComponentId(65)) // First bit in second uint @@ -161,6 +166,7 @@ func TestComponentBitTable_Test(t *testing.T) { func TestComponentBitTable_AllSet(t *testing.T) { table := NewComponentBitTable(200) entity := Entity(1) + table.Create(entity) // Set several components expectedComponents := []ComponentId{5, 10, 64, 128, 199} @@ -206,31 +212,39 @@ func TestComponentBitTable_AllSet(t *testing.T) { func TestComponentBitTable_extend(t *testing.T) { // Create a table with a small chunk size for testing - table := NewComponentBitTable(20) + table := NewComponentBitTable(65) // Set bits to force extension - for i := 0; i < pageSize*table.bitsetSize; i++ { - table.Set(Entity(i), ComponentId(1)) + for i := 0; i < pageSize; i++ { + e := Entity(i) + table.Create(e) + table.Set(e, ComponentId(1)) } if len(table.bits) > 1 { t.Errorf("Expected table to be not extended, got %d chunks", len(table.bits)) } - table.Set(Entity(pageSize*table.bitsetSize), ComponentId(1)) + e := Entity(pageSize) + table.Create(e) + table.Set(e, ComponentId(1)) if len(table.bits) != 2 { t.Errorf("Expected table to extend up to 2 chunks, got %d chunks", len(table.bits)) } - for i := pageSize * table.bitsetSize; i < pageSize*table.bitsetSize*2; i++ { - table.Set(Entity(i), ComponentId(1)) + for i := pageSize + 1; i < pageSize*2; i++ { + e := Entity(i) + table.Create(e) + table.Set(e, ComponentId(1)) } if len(table.bits) != 2 { t.Errorf("Expected table to extend up to 2 chunks, got %d chunks", len(table.bits)) } - table.Set(Entity(pageSize*table.bitsetSize*2), ComponentId(1)) + e = Entity(pageSize * 2) + table.Create(e) + table.Set(e, ComponentId(1)) if len(table.bits) != 3 { t.Errorf("Expected table to extend up to 3 chunks, got %d chunks", len(table.bits)) @@ -253,6 +267,7 @@ func TestComponentBitTable_EdgeCases(t *testing.T) { // Test setting across bit boundaries entity := Entity(42) + table.Create(entity) table.Set(entity, ComponentId(0)) // Last bit in first uint table.Set(entity, ComponentId(64)) // Last bit in first uint table.Set(entity, ComponentId(65)) // First bit in second uint @@ -269,6 +284,165 @@ func TestComponentBitTable_EdgeCases(t *testing.T) { } } +func TestComponentBitTable_Create(t *testing.T) { + table := NewComponentBitTable(100) + entity := Entity(42) + + // Create entity + table.Create(entity) + + // Verify entity is in lookup + bitsId, ok := table.lookup.Get(entity) + if !ok { + t.Fatalf("Entity %d not found in lookup after Create", entity) + } + + // Check that entity ID is stored in the first position of its bitset + chunkId := bitsId >> pageSizeShift + bitsetId := bitsId % pageSize + storedEntityId := Entity(table.bits[chunkId][bitsetId]) + + if storedEntityId != entity { + t.Errorf("Expected entity ID %d stored in bits, got %d", entity, storedEntityId) + } +} + +func TestComponentBitTable_Delete(t *testing.T) { + table := NewComponentBitTable(100) + entity := Entity(42) + table.Create(entity) + + // Set multiple components + table.Set(entity, ComponentId(5)) + table.Set(entity, ComponentId(10)) + + // Ensure components are set + if !table.Test(entity, ComponentId(5)) || !table.Test(entity, ComponentId(10)) { + t.Fatalf("Expected components to be set for entity %d", entity) + } + + // Delete the entity + table.Delete(entity) + + // Verify entity is no longer in lookup + _, ok := table.lookup.Get(entity) + if ok { + t.Errorf("Entity %d should be removed from lookup after deletion", entity) + } + + // Test should return false for deleted entity + if table.Test(entity, ComponentId(5)) || table.Test(entity, ComponentId(10)) { + t.Errorf("Test should return false for deleted entity %d", entity) + } +} + +func TestComponentBitTable_DeleteWithSwap(t *testing.T) { + table := NewComponentBitTable(100) + + // Create two entities + entity1 := Entity(1) + entity2 := Entity(2) + table.Create(entity1) + table.Create(entity2) + + // Set different components for each + table.Set(entity1, ComponentId(5)) + table.Set(entity1, ComponentId(10)) + table.Set(entity2, ComponentId(15)) + table.Set(entity2, ComponentId(20)) + + // Get entity2's bits ID before deletion of entity1 + entity2BitsId, _ := table.lookup.Get(entity2) + + // Delete the first entity - should swap with entity2 + table.Delete(entity1) + + // Verify entity1 is gone + _, ok := table.lookup.Get(entity1) + if ok { + t.Errorf("Entity %d should be removed from lookup", entity1) + } + + // Verify entity2's data is still accessible + if !table.Test(entity2, ComponentId(15)) || !table.Test(entity2, ComponentId(20)) { + t.Errorf("Entity %d should still have its components after swap", entity2) + } + + // Entity2's lookup entry should now point to entity1's old position + newEntity2BitsId, ok := table.lookup.Get(entity2) + if !ok { + t.Fatalf("Entity %d not found after swap", entity2) + } + + // Ensure the entity ID is correctly stored in the swapped position + chunkId := newEntity2BitsId >> pageSizeShift + bitsetId := newEntity2BitsId % pageSize + storedEntityId := Entity(table.bits[chunkId][bitsetId]) + if storedEntityId != entity2 { + t.Errorf("Entity ID %d not correctly stored in bits after swap, got %d", entity2, storedEntityId) + } + + // entity2 should have been moved to entity1's position + if newEntity2BitsId == entity2BitsId { + t.Errorf("Entity %d position should have changed after swap", entity2) + } + + if table.length != table.bitsetSize { + t.Errorf("Expected table length to be %d, got %d", table.bitsetSize, table.length) + } +} + +func TestComponentBitTable_MultipleOperations(t *testing.T) { + table := NewComponentBitTable(100) + + // Create, set, delete several entities in sequence + for i := 1; i <= 5; i++ { + entity := Entity(i) + table.Create(entity) + table.Set(entity, ComponentId(i)) + table.Set(entity, ComponentId(i+10)) + } + + // Delete entity 2 and 4 + table.Delete(Entity(2)) + table.Delete(Entity(4)) + + // Verify entities 1, 3, 5 still exist with correct components + for _, id := range []Entity{1, 3, 5} { + if !table.Test(id, ComponentId(int(id))) || !table.Test(id, ComponentId(int(id)+10)) { + t.Errorf("Entity %d should still have its components", id) + } + } + + // Verify entities 2 and 4 are gone + for _, id := range []Entity{2, 4} { + if _, ok := table.lookup.Get(id); ok { + t.Errorf("Entity %d should have been deleted", id) + } + } + + // Create new entities + for i := 6; i <= 7; i++ { + entity := Entity(i) + table.Create(entity) + table.Set(entity, ComponentId(i)) + } + + // Verify new entities have correct components + for i := 6; i <= 7; i++ { + entity := Entity(i) + if !table.Test(entity, ComponentId(i)) { + t.Errorf("Entity %d should have component %d set", entity, i) + } + } + + // The length should reflect the 5 entities (1, 3, 5, 6, 7) + expectedLen := 5 * table.bitsetSize + if table.length != expectedLen { + t.Errorf("Expected length %d, got %d", expectedLen, table.length) + } +} + // Helper function to check if slice contains a value func contains(s []ComponentId, id ComponentId) bool { for _, v := range s { @@ -278,3 +452,39 @@ func contains(s []ComponentId, id ComponentId) bool { } return false } + +func TestNextPowerOf2(t *testing.T) { + tests := []struct { + input int + expected int + }{ + {0, 0}, // Edge case: 0 stays 0 + {1, 1}, // Edge case: 1 stays 1 + {2, 2}, // Already power of 2 + {3, 4}, // Round up to 4 + {4, 4}, // Already power of 2 + {5, 8}, // Round up to 8 + {7, 8}, // Round up to 8 + {8, 8}, // Already power of 2 + {9, 16}, // Round up to 16 + {15, 16}, // Round up to 16 + {16, 16}, // Already power of 2 + {17, 32}, // Round up to 32 + {31, 32}, // Round up to 32 + {32, 32}, // Already power of 2 + {33, 64}, // Round up to 64 + {63, 64}, // Round up to 64 + {64, 64}, // Already power of 2 + {1023, 1024}, // Round up to 1024 + {1024, 1024}, // Already power of 2 + } + + for _, tt := range tests { + t.Run("", func(t *testing.T) { + got := nextPowerOf2(tt.input) + if got != tt.expected { + t.Errorf("nextPowerOf2(%d) = %d; expected %d", tt.input, got, tt.expected) + } + }) + } +} diff --git a/pkg/ecs/component-table_bench_test.go b/pkg/ecs/component-table_bench_test.go index cfde95af..09994cd6 100644 --- a/pkg/ecs/component-table_bench_test.go +++ b/pkg/ecs/component-table_bench_test.go @@ -3,36 +3,77 @@ package ecs import "testing" const testEntitiesLen = 100_000 +const maxComponentsLen = 1024 -func BenchmarkComponentBoolTable_SetAndTest(b *testing.B) { +func BenchmarkComponentBitTable_SetAndTest(b *testing.B) { // using a fixed maximum components length - table := NewComponentBoolTable(1024) + table := NewComponentBitTable(maxComponentsLen) b.ReportAllocs() for b.Loop() { for i := 0; i < testEntitiesLen; i++ { entity := Entity(i) - comp := ComponentId(i % 1024) + comp := ComponentId(i % maxComponentsLen) + table.Create(entity) table.Set(entity, comp) if !table.Test(entity, comp) { - b.Fatalf("ByteTable: expected entity %d to have component %d set", entity, comp) + b.Fatalf("BitTable: expected entity %d to have component %d set", entity, comp) } } } - } -func BenchmarkComponentBitTable_SetAndTest(b *testing.B) { +func BenchmarkComponentBitTable_Delete(b *testing.B) { // using a fixed maximum components length - table := NewComponentBitTable(1024) + table := NewComponentBitTable(maxComponentsLen) + // Setup - create and set components for all entities + for i := 0; i < testEntitiesLen; i++ { + entity := Entity(i) + table.Create(entity) + table.Set(entity, ComponentId(i%maxComponentsLen)) + } + b.ReportAllocs() + b.ResetTimer() + for b.Loop() { + // Delete all entities + for i := 0; i < testEntitiesLen; i++ { + entity := Entity(i) + table.Delete(entity) + } + + // Recreate for next iteration + for i := 0; i < testEntitiesLen; i++ { + entity := Entity(i) + table.Create(entity) + table.Set(entity, ComponentId(i%maxComponentsLen)) + } + } +} + +func BenchmarkComponentBitTable_AllSet(b *testing.B) { + table := NewComponentBitTable(maxComponentsLen) + // Prepare entities with multiple components each + for i := 0; i < testEntitiesLen; i++ { + entity := Entity(i) + table.Create(entity) + // Give each entity 3 components + table.Set(entity, ComponentId(i%maxComponentsLen)) + table.Set(entity, ComponentId((i+1)%maxComponentsLen)) + table.Set(entity, ComponentId((i+2)%maxComponentsLen)) + } + b.ReportAllocs() + b.ResetTimer() for b.Loop() { for i := 0; i < testEntitiesLen; i++ { entity := Entity(i) - comp := ComponentId(i % 1024) - table.Set(entity, comp) - if !table.Test(entity, comp) { - b.Fatalf("BitTable: expected entity %d to have component %d set", entity, comp) + count := 0 + table.AllSet(entity, func(id ComponentId) bool { + count++ + return true + }) + if count != 3 { + b.Fatalf("Expected 3 components, got %d", count) } } } diff --git a/pkg/ecs/entity-manager.go b/pkg/ecs/entity-manager.go index 2dbdc4fa..35269fca 100644 --- a/pkg/ecs/entity-manager.go +++ b/pkg/ecs/entity-manager.go @@ -75,6 +75,7 @@ func (e *EntityManager) Create() Entity { e.mx.Lock() defer e.mx.Unlock() var newId = e.generateEntityID() + e.componentBitTable.Create(newId) e.size++ @@ -88,6 +89,7 @@ func (e *EntityManager) Delete(entity Entity) { e.components[id].Delete(entity) return true }) + e.componentBitTable.Delete(entity) e.deletedEntityIDs = append(e.deletedEntityIDs, entity) e.size-- From 8bbb57498236b9691b9fe0efa9971cb33980e4e1 Mon Sep 17 00:00:00 2001 From: bitver Date: Wed, 30 Apr 2025 19:05:04 +0500 Subject: [PATCH 172/196] rocket science --- pkg/ecs/component-bit-table.go | 22 ++++++---------------- pkg/ecs/component-table_bench_test.go | 10 +++++----- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/pkg/ecs/component-bit-table.go b/pkg/ecs/component-bit-table.go index 46d140d7..3efa031e 100644 --- a/pkg/ecs/component-bit-table.go +++ b/pkg/ecs/component-bit-table.go @@ -21,20 +21,6 @@ import ( const uintShift = 7 - 64/bits.UintSize -// nextPowerOf2 rounds up to the next power of 2. -// For example: 5 -> 8, 17 -> 32, 32 -> 32 -func nextPowerOf2(v int) int { - v-- - v |= v >> 1 - v |= v >> 2 - v |= v >> 4 - v |= v >> 8 - v |= v >> 16 - v |= v >> 32 - v++ - return v -} - func NewComponentBitTable(maxComponentsLen int) ComponentBitTable { bitsetSize := ((maxComponentsLen - 1) / bits.UintSize) + 1 + 1 // 1 entry for the entity return ComponentBitTable{ @@ -132,12 +118,16 @@ func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) chunkId := bitsId / b.pageSize bitsetId := bitsId % b.pageSize for i := 1; i < b.bitsetSize; i++ { // i := 1 Skip the first entry (Entity) - for j := 0; j < bits.UintSize; j++ { - if (b.bits[chunkId][bitsetId+i]>>j)&1 == 1 { + set := b.bits[chunkId][bitsetId+i] + j := 0 + for set != 0 { + if set&1 == 1 { if !yield(ComponentId((i-1)*bits.UintSize + j)) { return } } + set >>= 1 + j++ } } } diff --git a/pkg/ecs/component-table_bench_test.go b/pkg/ecs/component-table_bench_test.go index 09994cd6..098b15af 100644 --- a/pkg/ecs/component-table_bench_test.go +++ b/pkg/ecs/component-table_bench_test.go @@ -56,10 +56,10 @@ func BenchmarkComponentBitTable_AllSet(b *testing.B) { for i := 0; i < testEntitiesLen; i++ { entity := Entity(i) table.Create(entity) - // Give each entity 3 components - table.Set(entity, ComponentId(i%maxComponentsLen)) - table.Set(entity, ComponentId((i+1)%maxComponentsLen)) - table.Set(entity, ComponentId((i+2)%maxComponentsLen)) + // Give each entity 100 components + for j := 0; j < 100; j++ { + table.Set(entity, ComponentId(j%maxComponentsLen)) + } } b.ReportAllocs() @@ -72,7 +72,7 @@ func BenchmarkComponentBitTable_AllSet(b *testing.B) { count++ return true }) - if count != 3 { + if count != 100 { b.Fatalf("Expected 3 components, got %d", count) } } From d291a00077f2c343d379c13353699be83dcc0958 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 2 May 2025 15:03:33 +0300 Subject: [PATCH 173/196] bench test the goasm and cgo calls --- internal/bench/aabb_test.go | 176 ++++++++++++++++++++++++++++++++ pkg/ecs/paged-array.go | 4 + pkg/gtime/time.go | 25 +++++ pkg/gtime/time_bench_test.go | 91 +++++++++++++++++ pkg/gtime/timestamp.c | 5 + pkg/gtime/timestamp.h | 8 ++ pkg/matrix/matrix.go | 36 +++++++ pkg/matrix/matrix_amd64.s | 83 +++++++++++++++ pkg/matrix/matrix_bench_test.go | 62 +++++++++++ pkg/timeasm/main.go | 15 +++ pkg/timeasm/timestamp.go | 63 ++++++++++++ pkg/timeasm/timestamp_amd64.s | 7 ++ pkg/timeasm/timestamp_asm.go | 18 ++++ 13 files changed, 593 insertions(+) create mode 100644 internal/bench/aabb_test.go create mode 100644 pkg/gtime/time.go create mode 100644 pkg/gtime/time_bench_test.go create mode 100644 pkg/gtime/timestamp.c create mode 100644 pkg/gtime/timestamp.h create mode 100644 pkg/matrix/matrix.go create mode 100644 pkg/matrix/matrix_amd64.s create mode 100644 pkg/matrix/matrix_bench_test.go create mode 100644 pkg/timeasm/main.go create mode 100644 pkg/timeasm/timestamp.go create mode 100644 pkg/timeasm/timestamp_amd64.s create mode 100644 pkg/timeasm/timestamp_asm.go diff --git a/internal/bench/aabb_test.go b/internal/bench/aabb_test.go new file mode 100644 index 00000000..6b7720a2 --- /dev/null +++ b/internal/bench/aabb_test.go @@ -0,0 +1,176 @@ +package bench + +import ( + "math" + "math/rand" + "testing" +) + +const ( + numCircles = 10000 + numAABBs = 10000 +) + +// Circle represents a circle with center (x, y) and radius r +type Circle struct { + X, Y, R float64 +} + +// AABB represents an axis-aligned bounding box +type AABB struct { + XMin, YMin, XMax, YMax float64 +} + +// circleCircleIntersect1 checks intersection using distance-based method +func circleCircleIntersect1(c1, c2 Circle) bool { + dx := c1.X - c2.X + dy := c1.Y - c2.Y + distance := math.Sqrt(dx*dx + dy*dy) + return distance < c1.R+c2.R +} + +// circleCircleIntersect2 checks intersection using squared distance +func circleCircleIntersect2(c1, c2 Circle) bool { + dx := c1.X - c2.X + dy := c1.Y - c2.Y + rSum := c1.R + c2.R + return dx*dx+dy*dy < rSum*rSum +} + +// circleAABBIntersect checks if circle intersects with AABB +func circleAABBIntersect(c Circle, a AABB) bool { + // Find closest point on AABB to circle center + closestX := math.Max(a.XMin, math.Min(c.X, a.XMax)) + closestY := math.Max(a.YMin, math.Min(c.Y, a.YMax)) + + // Calculate distance between closest point and circle center + dx := c.X - closestX + dy := c.Y - closestY + distance := math.Sqrt(dx*dx + dy*dy) + + return distance < c.R +} + +// aabbIntersect checks if two AABBs intersect +func aabbIntersect(a, b AABB) bool { + // Check if one box is to the left of the other + return a.XMax >= b.XMin && a.XMin <= b.XMax && a.YMax >= b.YMin && a.YMin <= b.YMax +} + +func aabbIntersect2(a, b AABB) bool { + // Check if one box is to the left of the other + if a.XMax < b.XMin || b.XMax < a.XMin { + return false + } + + return !(a.YMax < b.YMin || b.YMax < a.YMin) +} + +// generateRandomCircle generates a random circle +func generateRandomCircle() Circle { + return Circle{ + X: rand.Float64() * 100, + Y: rand.Float64() * 100, + R: rand.Float64()*10 + 5, + } +} + +// generateRandomAABB generates a random AABB +func generateRandomAABB() AABB { + x1 := rand.Float64() * 100 + y1 := rand.Float64() * 100 + w := rand.Float64()*20 + 10 + h := rand.Float64()*20 + 10 + return AABB{ + XMin: x1, + YMin: y1, + XMax: x1 + w, + YMax: y1 + h, + } +} + +func BenchmarkCircleCircleIntersect1(b *testing.B) { + rand.Seed(42) + circles := make([]Circle, numCircles) + for i := range circles { + circles[i] = generateRandomCircle() + } + + for b.Loop() { + lastIndex := len(circles) + for j := 0; j < lastIndex; j++ { + for k := j + 1; k < lastIndex; k++ { + circleCircleIntersect1(circles[j], circles[k]) + } + } + } +} + +func BenchmarkCircleCircleIntersect2(b *testing.B) { + rand.Seed(42) + circles := make([]Circle, numCircles) + for i := range circles { + circles[i] = generateRandomCircle() + } + + for b.Loop() { + lastIndex := len(circles) + for j := 0; j < lastIndex; j++ { + for k := j + 1; k < lastIndex; k++ { + circleCircleIntersect2(circles[j], circles[k]) + } + } + } +} + +// +//func BenchmarkCircleAABBIntersect(b *testing.B) { +// rand.Seed(42) +// circles := make([]Circle, numCircles) +// aabbs := make([]AABB, numAABBs) +// for i := range circles { +// circles[i] = generateRandomCircle() +// aabbs[i] = generateRandomAABB() +// } +// +// b.ResetTimer() +// for i := 0; i < b.N; i++ { +// for j := 0; j < len(aabbs); j++ { +// circleAABBIntersect(circles[j], aabbs[j]) +// } +// } +//} + +func BenchmarkAABBIntersect(b *testing.B) { + rand.Seed(42) + aabbs := make([]AABB, numAABBs) + for i := range aabbs { + aabbs[i] = generateRandomAABB() + } + + for b.Loop() { + lastIndex := len(aabbs) + for j := 0; j < lastIndex; j++ { + for k := j + 1; k < lastIndex; k++ { + aabbIntersect(aabbs[j], aabbs[k]) + } + } + } +} + +func BenchmarkAABBIntersect2(b *testing.B) { + rand.Seed(42) + aabbs := make([]AABB, numAABBs) + for i := range aabbs { + aabbs[i] = generateRandomAABB() + } + + for b.Loop() { + lastIndex := len(aabbs) + for j := 0; j < lastIndex; j++ { + for k := j + 1; k < lastIndex; k++ { + aabbIntersect2(aabbs[j], aabbs[k]) + } + } + } +} diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index b81f0332..49077ae4 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -12,6 +12,10 @@ import ( "sync" ) +/* +Go Slice tips and tricks - https://ueokande.github.io/go-slice-tricks/ +*/ + func NewPagedArray[T any]() (a PagedArray[T]) { a.book = make([]ArrayPage[T], initialBookSize) a.edpTasks = make([]EachDataTask[T], initialBookSize) diff --git a/pkg/gtime/time.go b/pkg/gtime/time.go new file mode 100644 index 00000000..976d38db --- /dev/null +++ b/pkg/gtime/time.go @@ -0,0 +1,25 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gtime + +/* +#include "timestamp.h" +*/ +import "C" + +// GetTimestampC возвращает текущий UNIX timestamp +func GetTimestampC() int64 { + return int64(C.get_system_timestamp()) +} diff --git a/pkg/gtime/time_bench_test.go b/pkg/gtime/time_bench_test.go new file mode 100644 index 00000000..73a3bcac --- /dev/null +++ b/pkg/gtime/time_bench_test.go @@ -0,0 +1,91 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gtime + +import ( + "gomp/pkg/timeasm" + "testing" + "time" +) + +const loadsize = 0 + +func workLoad(a int64) int64 { + var result int64 = 0 + for range a { + result += a * a + } + return result +} + +func BenchmarkAsm2(b *testing.B) { + b.ReportAllocs() + + for b.Loop() { + n := timeasm.Now() + _ = workLoad(loadsize) + _ = timeasm.Now() - n + } +} + +func BenchmarkAsm(b *testing.B) { + b.ReportAllocs() + + for b.Loop() { + n := timeasm.Cputicks() + _ = workLoad(loadsize) + _ = timeasm.Cputicks() - n + } +} + +func BenchmarkC(b *testing.B) { + b.ReportAllocs() + + for b.Loop() { + n := GetTimestampC() + _ = workLoad(loadsize) + _ = GetTimestampC() - n + } +} + +func BenchmarkTimeNow(b *testing.B) { + b.ReportAllocs() + + for b.Loop() { + n := time.Now() + _ = workLoad(loadsize) + _ = time.Since(n) + } +} + +func BenchmarkTimeNowUnix(b *testing.B) { + b.ReportAllocs() + + for b.Loop() { + n := time.Now().Unix() + _ = workLoad(loadsize) + _ = time.Now().Unix() - n + } +} + +// Опционально: сравнение с наносекундной точностью +func BenchmarkTimeNowUnixNano(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + n := time.Now().UnixNano() + _ = workLoad(loadsize) + _ = time.Now().UnixNano() - n + } +} diff --git a/pkg/gtime/timestamp.c b/pkg/gtime/timestamp.c new file mode 100644 index 00000000..db33fc36 --- /dev/null +++ b/pkg/gtime/timestamp.c @@ -0,0 +1,5 @@ +#include "timestamp.h" + +long long get_system_timestamp() { + return (long long)time(NULL); +} \ No newline at end of file diff --git a/pkg/gtime/timestamp.h b/pkg/gtime/timestamp.h new file mode 100644 index 00000000..9d135ed1 --- /dev/null +++ b/pkg/gtime/timestamp.h @@ -0,0 +1,8 @@ +#ifndef TIMESTAMP_H +#define TIMESTAMP_H + +#include + +long long get_system_timestamp(); + +#endif \ No newline at end of file diff --git a/pkg/matrix/matrix.go b/pkg/matrix/matrix.go new file mode 100644 index 00000000..c3c16566 --- /dev/null +++ b/pkg/matrix/matrix.go @@ -0,0 +1,36 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package matrix + +type V4 [4]float32 +type M4 [16]float32 + +//go:nosplit +func multiplyasm(data []V4, m M4) + +func M4MultiplyV4(m M4, v V4) V4 { + return V4{ + v[0]*m[0] + v[1]*m[4] + v[2]*m[8] + v[3]*m[12], + v[0]*m[1] + v[1]*m[5] + v[2]*m[9] + v[3]*m[13], + v[0]*m[2] + v[1]*m[6] + v[2]*m[10] + v[3]*m[14], + v[0]*m[3] + v[1]*m[7] + v[2]*m[11] + v[3]*m[15], + } +} + +func multiply(data []V4, m M4) { + for i, v := range data { + data[i] = M4MultiplyV4(m, v) + } +} diff --git a/pkg/matrix/matrix_amd64.s b/pkg/matrix/matrix_amd64.s new file mode 100644 index 00000000..308294dd --- /dev/null +++ b/pkg/matrix/matrix_amd64.s @@ -0,0 +1,83 @@ +// func multiplyasm(data []V4, m M4) +// +// memory layout of the stack relative to FP +// +0 data slice ptr +// +8 data slice len +// +16 data slice cap +// +24 m[0] | m[1] +// +32 m[2] | m[3] +// +40 m[4] | m[5] +// +48 m[6] | m[7] +// +56 m[8] | m[9] +// +64 m[10] | m[11] +// +72 m[12] | m[13] +// +80 m[14] | m[15] + +#include "textflag.h" + +TEXT ·multiplyasm(SB),NOSPLIT,$0 + // data ptr + MOVQ data+0(FP), CX + // data len + MOVQ data+8(FP), SI + // index into data + MOVQ $0, AX + // return early if zero length + CMPQ AX, SI + JE END + // load the matrix into 128-bit wide xmm registers + // load [m[0], m[1], m[2], m[3]] into xmm0 + MOVUPS m+24(FP), X0 + // load [m[4], m[5], m[6], m[7]] into xmm1 + MOVUPS m+40(FP), X1 + // load [m[8], m[9], m[10], m[11]] into xmm2 + MOVUPS m+56(FP), X2 + // load [m[12], m[13], m[14], m[15]] into xmm3 + MOVUPS m+72(FP), X3 +LOOP: + // load each component of the vector into xmm registers + // load data[i][0] (x) into xmm4 + MOVSS 0(CX), X4 + // load data[i][1] (y) into xmm5 + MOVSS 4(CX), X5 + // load data[i][2] (z) into xmm6 + MOVSS 8(CX), X6 + // load data[i][3] (w) into xmm7 + MOVSS 12(CX), X7 + // copy each component of the matrix across each register + // [0, 0, 0, x] => [x, x, x, x] + SHUFPS $0, X4, X4 + // [0, 0, 0, y] => [y, y, y, y] + SHUFPS $0, X5, X5 + // [0, 0, 0, z] => [z, z, z, z] + SHUFPS $0, X6, X6 + // [0, 0, 0, w] => [w, w, w, w] + SHUFPS $0, X7, X7 + // xmm4 = [m[0], m[1], m[2], m[3]] .* [x, x, x, x] + MULPS X0, X4 + // xmm5 = [m[4], m[5], m[6], m[7]] .* [y, y, y, y] + MULPS X1, X5 + // xmm6 = [m[8], m[9], m[10], m[11]] .* [z, z, z, z] + MULPS X2, X6 + // xmm7 = [m[12], m[13], m[14], m[15]] .* [w, w, w, w] + MULPS X3, X7 + // xmm4 = xmm4 + xmm5 + ADDPS X5, X4 + // xmm4 = xmm4 + xmm6 + ADDPS X6, X4 + // xmm4 = xmm4 + xmm7 + ADDPS X7, X4 + // data[i] = xmm4 + MOVNTPS X4, 0(CX) + // data++ + ADDQ $16, CX + // i++ + INCQ AX + // if i >= len(data) break + CMPQ AX, SI + JLT LOOP +END: + // since we use a non-temporal write (MOVNTPS) + // make sure all writes are visible before we leave the function + SFENCE + RET diff --git a/pkg/matrix/matrix_bench_test.go b/pkg/matrix/matrix_bench_test.go new file mode 100644 index 00000000..e89acc4b --- /dev/null +++ b/pkg/matrix/matrix_bench_test.go @@ -0,0 +1,62 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package matrix + +import ( + "testing" +) + +// createSampleM4 generates a sample 4x4 identity matrix. +func createSampleM4() M4 { + return M4{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, + } +} + +// createSampleData generates a slice of sample V4 vectors. +func createSampleData(size int) []V4 { + data := make([]V4, size) + for i := range data { + data[i] = V4{float32(i), float32(i + 1), float32(i + 2), float32(i + 3)} + } + return data +} + +// BenchmarkMultiply benchmarks the multiply function with a 1000-element slice. +func BenchmarkMultiply(b *testing.B) { + m := createSampleM4() + backupData := createSampleData(1000) // Sample data with 1000 vectors + data := make([]V4, len(backupData)) // Preallocate to avoid allocations in loop + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(data, backupData) // Reset data to original state + multiply(data, m) + } +} + +// BenchmarkMultiplyAsm benchmarks the multiply function with a 1000-element slice. +func BenchmarkMultiplyAsm(b *testing.B) { + m := createSampleM4() + backupData := createSampleData(1000) // Sample data with 1000 vectors + data := make([]V4, len(backupData)) // Preallocate to avoid allocations in loop + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(data, backupData) // Reset data to original state + multiplyasm(data, m) + } +} diff --git a/pkg/timeasm/main.go b/pkg/timeasm/main.go new file mode 100644 index 00000000..b2900b84 --- /dev/null +++ b/pkg/timeasm/main.go @@ -0,0 +1,15 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package timeasm diff --git a/pkg/timeasm/timestamp.go b/pkg/timeasm/timestamp.go new file mode 100644 index 00000000..b95c893c --- /dev/null +++ b/pkg/timeasm/timestamp.go @@ -0,0 +1,63 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package timeasm + +import ( + "math/bits" + "time" + _ "unsafe" // Для go:linkname +) + +var ( + tscFrequency uint64 + baseTimeUnix int64 + tscFrequencyShift int +) + +//func init() { +// calibrateTSC() +//} + +func calibrateTSC() { + baseTime := time.Now() + baseTimeUnix = baseTime.UnixNano() + ticksStart := Cputicks() + time.Sleep(100 * time.Millisecond) + endTime := time.Now() + ticksEnd := Cputicks() + + tscFrequency = (ticksEnd - ticksStart) * 10 / uint64(endTime.Sub(baseTime).Nanoseconds()) + tscFrequencyShift = bits.Len64(tscFrequency) - 1 +} + +func GetTimestamp() uint64 { + return Cputicks() +} + +var baseNano int64 = 0 +var baseTsc uint64 +var scale float64 + +func Now() int64 { + if baseNano == 0 { + baseNano = time.Now().UnixNano() + baseTsc = Cputicks() + time.Sleep(1e7) + rn := time.Now().UnixNano() + ct := Cputicks() + scale = float64(rn-baseNano) / float64(ct-baseTsc) + } + return baseNano + int64(float64(Cputicks()-baseTsc)*scale) +} diff --git a/pkg/timeasm/timestamp_amd64.s b/pkg/timeasm/timestamp_amd64.s new file mode 100644 index 00000000..99f68a88 --- /dev/null +++ b/pkg/timeasm/timestamp_amd64.s @@ -0,0 +1,7 @@ +// func Cputicks(void) (n uint64) +TEXT ·Cputicks(SB),7,$0 + RDTSC + SHLQ $32, DX + ADDQ DX, AX + MOVQ AX, n+0(FP) + RET diff --git a/pkg/timeasm/timestamp_asm.go b/pkg/timeasm/timestamp_asm.go new file mode 100644 index 00000000..56404cca --- /dev/null +++ b/pkg/timeasm/timestamp_asm.go @@ -0,0 +1,18 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +// timestamp_asm.go +package timeasm + +func Cputicks() (t uint64) From ec96dab2400e9999c18105d28e45eec64288df9f Mon Sep 17 00:00:00 2001 From: bitver Date: Fri, 2 May 2025 18:50:21 +0500 Subject: [PATCH 174/196] wanna perf? --- pkg/ecs/component-bit-table.go | 113 ++++++++++--------- pkg/ecs/component-bit-table_test.go | 152 ++++++++------------------ pkg/ecs/component-table_bench_test.go | 53 +++++++-- pkg/ecs/paged-map.go | 12 ++ 4 files changed, 163 insertions(+), 167 deletions(-) diff --git a/pkg/ecs/component-bit-table.go b/pkg/ecs/component-bit-table.go index 3efa031e..2307b9d9 100644 --- a/pkg/ecs/component-bit-table.go +++ b/pkg/ecs/component-bit-table.go @@ -19,63 +19,70 @@ import ( "math/bits" ) -const uintShift = 7 - 64/bits.UintSize +const ( + uintShift = 7 - 64/bits.UintSize + pageSizeMask = pageSize - 1 +) func NewComponentBitTable(maxComponentsLen int) ComponentBitTable { - bitsetSize := ((maxComponentsLen - 1) / bits.UintSize) + 1 + 1 // 1 entry for the entity + bitsetSize := ((maxComponentsLen - 1) / bits.UintSize) + 1 return ComponentBitTable{ - bits: make([][]uint, 0, initialBookSize), - lookup: NewPagedMap[Entity, int](), - bitsetSize: bitsetSize, - pageSize: bitsetSize * pageSize, + bitsetsBook: make([][]uint, 0, initialBookSize), + entitiesBook: make([][]Entity, 0, initialBookSize), + lookup: NewPagedMap[Entity, int](), + bitsetSize: bitsetSize, + pageSize: bitsetSize * pageSize, } } type ComponentBitTable struct { - bits [][]uint - lookup PagedMap[Entity, int] - length int - bitsetSize int - pageSize int + bitsetsBook [][]uint + entitiesBook [][]Entity + lookup PagedMap[Entity, int] + length int + bitsetSize int + pageSize int } func (b *ComponentBitTable) Create(entity Entity) { - bitsId, ok := b.lookup.Get(entity) - assert.False(ok, "entity already exists") + assert.False(b.lookup.Has(entity), "entity already exists") + b.extend() - bitsId = b.length + bitsId := b.length b.lookup.Set(entity, bitsId) - chunkId := bitsId / b.pageSize - bitsetId := bitsId % b.pageSize - b.bits[chunkId][bitsetId] = uint(entity) - b.length += b.bitsetSize + pageId, entityId := b.getPageIDAndEntityIndex(bitsId) + b.entitiesBook[pageId][entityId] = entity + b.length++ } func (b *ComponentBitTable) Delete(entity Entity) { - bitsId, ok := b.lookup.Get(entity) + bitsetIndex, ok := b.lookup.Get(entity) assert.True(ok, "entity not found") // Get the index of the last entity - lastIndex := b.length - b.bitsetSize + lastIndex := b.length - 1 // If this is not the last entity, swap with the last one - if bitsId != lastIndex { - lastChunkId := lastIndex / b.pageSize - lastBitsetId := lastIndex % b.pageSize - deleteChunkId := bitsId / b.pageSize - deleteBitsetId := bitsId % b.pageSize + if bitsetIndex != lastIndex { + lastPageId, lastEntityId := b.getPageIDAndEntityIndex(lastIndex) + lastBitsetId := lastEntityId * b.bitsetSize + deletePageId, deleteEntityId := b.getPageIDAndEntityIndex(bitsetIndex) + deleteBitsetId := deleteEntityId * b.bitsetSize - lastEntity := b.bits[lastChunkId][lastBitsetId] + // Copy bitset from last entity to the deleted entity's position for i := 0; i < b.bitsetSize; i++ { - b.bits[deleteChunkId][deleteBitsetId+i] = b.bits[lastChunkId][lastBitsetId+i] - b.bits[lastChunkId][lastBitsetId+i] = 0 + b.bitsetsBook[deletePageId][deleteBitsetId+i] = b.bitsetsBook[lastPageId][lastBitsetId+i] + b.bitsetsBook[lastPageId][lastBitsetId+i] = 0 } - b.lookup.Set(Entity(lastEntity), bitsId) + // Get the last entity and update its position in lookup + lastEntity := b.entitiesBook[lastPageId][lastEntityId] + b.entitiesBook[deletePageId][deleteEntityId] = lastEntity + b.lookup.Set(lastEntity, bitsetIndex) } b.lookup.Delete(entity) - b.length -= b.bitsetSize + b.length-- } // Set sets the bit at the given index to 1. @@ -83,20 +90,19 @@ func (b *ComponentBitTable) Set(entity Entity, componentId ComponentId) { bitsId, ok := b.lookup.Get(entity) assert.True(ok, "entity not found") - chunkId := bitsId / b.pageSize - bitsetId := bitsId % b.pageSize - offset := int(componentId>>uintShift) + 1 // +1 to skip the first Entity entry - b.bits[chunkId][bitsetId+offset] |= 1 << (componentId % bits.UintSize) + pageId, bitsetId := b.getPageIDAndBitsetIndex(bitsId) + offset := int(componentId) >> uintShift + b.bitsetsBook[pageId][bitsetId+offset] |= 1 << (componentId % bits.UintSize) } // Unset clears the bit at the given index (sets it to 0). func (b *ComponentBitTable) Unset(entity Entity, componentId ComponentId) { bitsId, ok := b.lookup.Get(entity) assert.True(ok, "entity not found") - chunkId := bitsId / b.pageSize - bitsetId := bitsId % b.pageSize - offset := int(componentId>>uintShift) + 1 // +1 to skip the first Entity entry - b.bits[chunkId][bitsetId+offset] &= ^(1 << (componentId % bits.UintSize)) + + pageId, bitsetId := b.getPageIDAndBitsetIndex(bitsId) + offset := int(componentId) >> uintShift + b.bitsetsBook[pageId][bitsetId+offset] &= ^(1 << (componentId % bits.UintSize)) } func (b *ComponentBitTable) Test(entity Entity, componentId ComponentId) bool { @@ -104,10 +110,9 @@ func (b *ComponentBitTable) Test(entity Entity, componentId ComponentId) bool { if !ok { return false } - chunkId := bitsId / b.pageSize - bitsetId := bitsId % b.pageSize - offset := int(componentId>>uintShift) + 1 // +1 to skip the first Entity entry - return (b.bits[chunkId][bitsetId+offset] & (1 << (componentId % bits.UintSize))) != 0 + pageId, bitsetId := b.getPageIDAndBitsetIndex(bitsId) + offset := int(componentId) >> uintShift + return (b.bitsetsBook[pageId][bitsetId+offset] & (1 << (componentId % bits.UintSize))) != 0 } func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) { @@ -115,14 +120,13 @@ func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) if !ok { return } - chunkId := bitsId / b.pageSize - bitsetId := bitsId % b.pageSize - for i := 1; i < b.bitsetSize; i++ { // i := 1 Skip the first entry (Entity) - set := b.bits[chunkId][bitsetId+i] + pageId, bitsetId := b.getPageIDAndBitsetIndex(bitsId) + for i := 0; i < b.bitsetSize; i++ { + set := b.bitsetsBook[pageId][bitsetId+i] j := 0 for set != 0 { if set&1 == 1 { - if !yield(ComponentId((i-1)*bits.UintSize + j)) { + if !yield(ComponentId(i*bits.UintSize + j)) { return } } @@ -133,8 +137,17 @@ func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) } func (b *ComponentBitTable) extend() { - lastChunkId := b.length / b.pageSize - if lastChunkId == len(b.bits) && b.length%b.pageSize == 0 { - b.bits = append(b.bits, make([]uint, b.pageSize)) + lastChunkId, lastEntityId := b.getPageIDAndEntityIndex(b.length) + if lastChunkId == len(b.bitsetsBook) && lastEntityId == 0 { + b.bitsetsBook = append(b.bitsetsBook, make([]uint, b.pageSize)) + b.entitiesBook = append(b.entitiesBook, make([]Entity, pageSize)) } } + +func (b *ComponentBitTable) getPageIDAndBitsetIndex(index int) (int, int) { + return index >> pageSizeShift, (index & pageSizeMask) * b.bitsetSize +} + +func (b *ComponentBitTable) getPageIDAndEntityIndex(index int) (int, int) { + return index >> pageSizeShift, index & pageSizeMask +} diff --git a/pkg/ecs/component-bit-table_test.go b/pkg/ecs/component-bit-table_test.go index d374e85c..a5eae592 100644 --- a/pkg/ecs/component-bit-table_test.go +++ b/pkg/ecs/component-bit-table_test.go @@ -37,12 +37,8 @@ func TestNewComponentBitTable(t *testing.T) { t.Run(tt.name, func(t *testing.T) { table := NewComponentBitTable(tt.maxComponentsLen) - //if table.bitsetSize != tt.expectedBitsetSize+1 { - // t.Errorf("Expected bitsetSize %d, got %d", tt.expectedBitsetSize, table.bitsetSize) - //} - - if cap(table.bits) != initialBookSize { - t.Errorf("Expected %d preallocated chunks, got %d", initialBookSize, cap(table.bits)) + if cap(table.bitsetsBook) != initialBookSize { + t.Errorf("Expected %d preallocated chunks, got %d", initialBookSize, cap(table.bitsetsBook)) } }) } @@ -62,12 +58,11 @@ func TestComponentBitTable_Set(t *testing.T) { t.Fatalf("Entity %d not found in lookup", entity1) } - chunkId := bitsId / pageSize - bitsetId := bitsId % pageSize - offset := int(ComponentId(5)/bits.UintSize) + 1 + pageId, bitsetId := table.getPageIDAndBitsetIndex(bitsId) + offset := int(ComponentId(5) >> uintShift) mask := uint(1 << (ComponentId(5) % bits.UintSize)) - if (table.bits[chunkId][bitsetId+offset] & mask) == 0 { + if (table.bitsetsBook[pageId][bitsetId+offset] & mask) == 0 { t.Errorf("Expected bit to be set for entity %d, component %d", entity1, 5) } @@ -94,20 +89,19 @@ func TestComponentBitTable_Unset(t *testing.T) { // Verify bit was unset bitsId, _ := table.lookup.Get(entity) - chunkId := bitsId / pageSize - bitsetId := bitsId % pageSize - offset := int(ComponentId(5) / bits.UintSize) + pageId, bitsetId := table.getPageIDAndBitsetIndex(bitsId) + offset := int(ComponentId(5) >> uintShift) mask := uint(1 << (ComponentId(5) % bits.UintSize)) - if (table.bits[chunkId][bitsetId+offset] & mask) != 0 { + if (table.bitsetsBook[pageId][bitsetId+offset] & mask) != 0 { t.Errorf("Expected bit to be unset for entity %d, component %d", entity, 5) } // Verify other bit is still set - offset = int(ComponentId(10)/bits.UintSize) + 1 + offset = int(ComponentId(10) >> uintShift) mask = uint(1 << (ComponentId(10) % bits.UintSize)) - if (table.bits[chunkId][bitsetId+offset] & mask) == 0 { + if (table.bitsetsBook[pageId][bitsetId+offset] & mask) == 0 { t.Errorf("Expected bit to still be set for entity %d, component %d", entity, 10) } } @@ -149,8 +143,8 @@ func TestComponentBitTable_Test(t *testing.T) { entity2 := Entity(43) table.Create(entity2) table.Set(entity2, ComponentId(0)) - table.Set(entity2, ComponentId(64)) // Last bit in first uint - table.Set(entity2, ComponentId(65)) // First bit in second uint + table.Set(entity2, ComponentId(64)) // First bit in second uint + table.Set(entity2, ComponentId(65)) // Second bit in second uint if !table.Test(entity2, ComponentId(0)) { t.Error("Test should return true for set component at uint boundary (0)") @@ -212,42 +206,32 @@ func TestComponentBitTable_AllSet(t *testing.T) { func TestComponentBitTable_extend(t *testing.T) { // Create a table with a small chunk size for testing - table := NewComponentBitTable(65) - // Set bits to force extension + table := NewComponentBitTable(bits.UintSize) + + // Create entities to fill the first chunk for i := 0; i < pageSize; i++ { e := Entity(i) table.Create(e) - table.Set(e, ComponentId(1)) + table.Set(e, ComponentId(i%bits.UintSize)) } - - if len(table.bits) > 1 { - t.Errorf("Expected table to be not extended, got %d chunks", len(table.bits)) + if len(table.bitsetsBook) != 1 { + t.Errorf("Expected table to extend, got %d chunks", len(table.bitsetsBook)) } - e := Entity(pageSize) - table.Create(e) - table.Set(e, ComponentId(1)) - - if len(table.bits) != 2 { - t.Errorf("Expected table to extend up to 2 chunks, got %d chunks", len(table.bits)) + // Create entity to force extension + table.Create(pageSize) + if len(table.bitsetsBook) <= 1 { + t.Errorf("Expected table to be extended, got %d chunks", len(table.bitsetsBook)) } - for i := pageSize + 1; i < pageSize*2; i++ { + // Create more entities + for i := pageSize + 1; i < (pageSize*2)+1; i++ { e := Entity(i) table.Create(e) table.Set(e, ComponentId(1)) } - - if len(table.bits) != 2 { - t.Errorf("Expected table to extend up to 2 chunks, got %d chunks", len(table.bits)) - } - - e = Entity(pageSize * 2) - table.Create(e) - table.Set(e, ComponentId(1)) - - if len(table.bits) != 3 { - t.Errorf("Expected table to extend up to 3 chunks, got %d chunks", len(table.bits)) + if len(table.bitsetsBook) <= 2 { + t.Errorf("Expected table to extend beyond 2 chunks, got %d chunks", len(table.bitsetsBook)) } } @@ -268,19 +252,19 @@ func TestComponentBitTable_EdgeCases(t *testing.T) { // Test setting across bit boundaries entity := Entity(42) table.Create(entity) - table.Set(entity, ComponentId(0)) // Last bit in first uint - table.Set(entity, ComponentId(64)) // Last bit in first uint - table.Set(entity, ComponentId(65)) // First bit in second uint + table.Set(entity, ComponentId(0)) // First bit in first uint + table.Set(entity, ComponentId(63)) // Last bit in first uint + table.Set(entity, ComponentId(64)) // First bit in second uint - // Verify both bits + // Verify all bits var found []ComponentId table.AllSet(entity, func(id ComponentId) bool { found = append(found, id) return true }) - if len(found) != 3 || !contains(found, ComponentId(0)) || !contains(found, ComponentId(64)) || !contains(found, ComponentId(65)) { - t.Errorf("Expected components 0, 64 and 65, got %v", found) + if len(found) != 3 || !contains(found, ComponentId(0)) || !contains(found, ComponentId(63)) || !contains(found, ComponentId(64)) { + t.Errorf("Expected components 0, 63 and 64, got %v", found) } } @@ -292,18 +276,18 @@ func TestComponentBitTable_Create(t *testing.T) { table.Create(entity) // Verify entity is in lookup - bitsId, ok := table.lookup.Get(entity) + _, ok := table.lookup.Get(entity) if !ok { t.Fatalf("Entity %d not found in lookup after Create", entity) } - // Check that entity ID is stored in the first position of its bitset - chunkId := bitsId >> pageSizeShift - bitsetId := bitsId % pageSize - storedEntityId := Entity(table.bits[chunkId][bitsetId]) + // Check that entity ID is stored in entities book + bitsId, _ := table.lookup.Get(entity) + pageId, entityId := table.getPageIDAndEntityIndex(bitsId) - if storedEntityId != entity { - t.Errorf("Expected entity ID %d stored in bits, got %d", entity, storedEntityId) + if table.entitiesBook[pageId][entityId] != entity { + t.Errorf("Expected entity ID %d stored in entities book, got %d", + entity, table.entitiesBook[pageId][entityId]) } } @@ -374,21 +358,16 @@ func TestComponentBitTable_DeleteWithSwap(t *testing.T) { t.Fatalf("Entity %d not found after swap", entity2) } - // Ensure the entity ID is correctly stored in the swapped position - chunkId := newEntity2BitsId >> pageSizeShift - bitsetId := newEntity2BitsId % pageSize - storedEntityId := Entity(table.bits[chunkId][bitsetId]) - if storedEntityId != entity2 { - t.Errorf("Entity ID %d not correctly stored in bits after swap, got %d", entity2, storedEntityId) - } - // entity2 should have been moved to entity1's position if newEntity2BitsId == entity2BitsId { t.Errorf("Entity %d position should have changed after swap", entity2) } - if table.length != table.bitsetSize { - t.Errorf("Expected table length to be %d, got %d", table.bitsetSize, table.length) + // Ensure entity is stored in the entity book + pageId, entityId := table.getPageIDAndEntityIndex(newEntity2BitsId) + if table.entitiesBook[pageId][entityId] != entity2 { + t.Errorf("Entity ID %d not correctly stored after swap, got %d", + entity2, table.entitiesBook[pageId][entityId]) } } @@ -436,10 +415,9 @@ func TestComponentBitTable_MultipleOperations(t *testing.T) { } } - // The length should reflect the 5 entities (1, 3, 5, 6, 7) - expectedLen := 5 * table.bitsetSize - if table.length != expectedLen { - t.Errorf("Expected length %d, got %d", expectedLen, table.length) + // The length should be 5 (entities 1, 3, 5, 6, 7) + if table.length != 5 { + t.Errorf("Expected length 5, got %d", table.length) } } @@ -452,39 +430,3 @@ func contains(s []ComponentId, id ComponentId) bool { } return false } - -func TestNextPowerOf2(t *testing.T) { - tests := []struct { - input int - expected int - }{ - {0, 0}, // Edge case: 0 stays 0 - {1, 1}, // Edge case: 1 stays 1 - {2, 2}, // Already power of 2 - {3, 4}, // Round up to 4 - {4, 4}, // Already power of 2 - {5, 8}, // Round up to 8 - {7, 8}, // Round up to 8 - {8, 8}, // Already power of 2 - {9, 16}, // Round up to 16 - {15, 16}, // Round up to 16 - {16, 16}, // Already power of 2 - {17, 32}, // Round up to 32 - {31, 32}, // Round up to 32 - {32, 32}, // Already power of 2 - {33, 64}, // Round up to 64 - {63, 64}, // Round up to 64 - {64, 64}, // Already power of 2 - {1023, 1024}, // Round up to 1024 - {1024, 1024}, // Already power of 2 - } - - for _, tt := range tests { - t.Run("", func(t *testing.T) { - got := nextPowerOf2(tt.input) - if got != tt.expected { - t.Errorf("nextPowerOf2(%d) = %d; expected %d", tt.input, got, tt.expected) - } - }) - } -} diff --git a/pkg/ecs/component-table_bench_test.go b/pkg/ecs/component-table_bench_test.go index 098b15af..3eff8527 100644 --- a/pkg/ecs/component-table_bench_test.go +++ b/pkg/ecs/component-table_bench_test.go @@ -2,7 +2,7 @@ package ecs import "testing" -const testEntitiesLen = 100_000 +const testEntitiesLen Entity = 100_000 const maxComponentsLen = 1024 func BenchmarkComponentBitTable_SetAndTest(b *testing.B) { @@ -10,14 +10,43 @@ func BenchmarkComponentBitTable_SetAndTest(b *testing.B) { table := NewComponentBitTable(maxComponentsLen) b.ReportAllocs() for b.Loop() { - for i := 0; i < testEntitiesLen; i++ { - entity := Entity(i) + for i := Entity(0); i < testEntitiesLen; i++ { comp := ComponentId(i % maxComponentsLen) - table.Create(entity) - table.Set(entity, comp) - if !table.Test(entity, comp) { - b.Fatalf("BitTable: expected entity %d to have component %d set", entity, comp) + table.Create(i) + table.Set(i, comp) + if !table.Test(i, comp) { + b.Fatalf("BitTable: expected entity %d to have component %d set", i, comp) + } + } + b.StopTimer() + for i := Entity(0); i < testEntitiesLen; i++ { + table.Delete(i) + } + b.StartTimer() + } +} + +func BenchmarkComponentBitTable_SetTestDelete(b *testing.B) { + table := NewComponentBitTable(maxComponentsLen) + //for i := Entity(1); i < testEntitiesLen; i++ { + // table.Create(Entity(i)) + //} + // using a fixed maximum components length + b.ReportAllocs() + for b.Loop() { + b.StopTimer() + for i := Entity(0); i < testEntitiesLen; i++ { + table.Create(i) + } + b.StartTimer() + + for i := Entity(0); i < testEntitiesLen; i++ { + comp := ComponentId(i % maxComponentsLen) + table.Set(i, comp) + if !table.Test(i, comp) { + b.Fatalf("BitTable: expected entity %d to have component %d set", i, comp) } + table.Delete(i) } } } @@ -26,7 +55,7 @@ func BenchmarkComponentBitTable_Delete(b *testing.B) { // using a fixed maximum components length table := NewComponentBitTable(maxComponentsLen) // Setup - create and set components for all entities - for i := 0; i < testEntitiesLen; i++ { + for i := Entity(0); i < testEntitiesLen; i++ { entity := Entity(i) table.Create(entity) table.Set(entity, ComponentId(i%maxComponentsLen)) @@ -36,13 +65,13 @@ func BenchmarkComponentBitTable_Delete(b *testing.B) { b.ResetTimer() for b.Loop() { // Delete all entities - for i := 0; i < testEntitiesLen; i++ { + for i := Entity(0); i < testEntitiesLen; i++ { entity := Entity(i) table.Delete(entity) } // Recreate for next iteration - for i := 0; i < testEntitiesLen; i++ { + for i := Entity(0); i < testEntitiesLen; i++ { entity := Entity(i) table.Create(entity) table.Set(entity, ComponentId(i%maxComponentsLen)) @@ -53,7 +82,7 @@ func BenchmarkComponentBitTable_Delete(b *testing.B) { func BenchmarkComponentBitTable_AllSet(b *testing.B) { table := NewComponentBitTable(maxComponentsLen) // Prepare entities with multiple components each - for i := 0; i < testEntitiesLen; i++ { + for i := Entity(0); i < testEntitiesLen; i++ { entity := Entity(i) table.Create(entity) // Give each entity 100 components @@ -65,7 +94,7 @@ func BenchmarkComponentBitTable_AllSet(b *testing.B) { b.ReportAllocs() b.ResetTimer() for b.Loop() { - for i := 0; i < testEntitiesLen; i++ { + for i := Entity(0); i < testEntitiesLen; i++ { entity := Entity(i) count := 0 table.AllSet(entity, func(id ComponentId) bool { diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 60f51f09..dc190478 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -74,6 +74,18 @@ func (m *PagedMap[K, V]) Delete(key K) { } } +func (m *PagedMap[K, V]) Has(key K) bool { + pageID, index := m.getPageIDAndIndex(key) + if pageID >= len(m.book) { + return false + } + page := &m.book[pageID] + if page.data == nil { + return false + } + return page.data[index].ok +} + func (m *PagedMap[K, V]) getPageIDAndIndex(key K) (pageID int, index int) { return int(uint64(key) >> pageSizeShift), int(uint64(key) % pageSize) } From cf84f1bc1fc24b872c26bf879f2e7feffa70b27b Mon Sep 17 00:00:00 2001 From: bitver Date: Fri, 2 May 2025 22:42:08 +0500 Subject: [PATCH 175/196] micro optimizations --- pkg/ecs/component-bit-table.go | 11 +++--- pkg/ecs/paged-array.go | 70 ++++++++++++++++++++-------------- pkg/ecs/paged-map.go | 1 + 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/pkg/ecs/component-bit-table.go b/pkg/ecs/component-bit-table.go index 2307b9d9..2802663f 100644 --- a/pkg/ecs/component-bit-table.go +++ b/pkg/ecs/component-bit-table.go @@ -20,15 +20,14 @@ import ( ) const ( - uintShift = 7 - 64/bits.UintSize - pageSizeMask = pageSize - 1 + uintShift = 7 - 64/bits.UintSize ) func NewComponentBitTable(maxComponentsLen int) ComponentBitTable { bitsetSize := ((maxComponentsLen - 1) / bits.UintSize) + 1 return ComponentBitTable{ bitsetsBook: make([][]uint, 0, initialBookSize), - entitiesBook: make([][]Entity, 0, initialBookSize), + entitiesBook: make([]*entityArray, 0, initialBookSize), lookup: NewPagedMap[Entity, int](), bitsetSize: bitsetSize, pageSize: bitsetSize * pageSize, @@ -37,13 +36,15 @@ func NewComponentBitTable(maxComponentsLen int) ComponentBitTable { type ComponentBitTable struct { bitsetsBook [][]uint - entitiesBook [][]Entity + entitiesBook []*entityArray lookup PagedMap[Entity, int] length int bitsetSize int pageSize int } +type entityArray [pageSize]Entity + func (b *ComponentBitTable) Create(entity Entity) { assert.False(b.lookup.Has(entity), "entity already exists") @@ -140,7 +141,7 @@ func (b *ComponentBitTable) extend() { lastChunkId, lastEntityId := b.getPageIDAndEntityIndex(b.length) if lastChunkId == len(b.bitsetsBook) && lastEntityId == 0 { b.bitsetsBook = append(b.bitsetsBook, make([]uint, b.pageSize)) - b.entitiesBook = append(b.entitiesBook, make([]Entity, pageSize)) + b.entitiesBook = append(b.entitiesBook, &entityArray{}) } } diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index b81f0332..e5e93c5b 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -13,10 +13,12 @@ import ( ) func NewPagedArray[T any]() (a PagedArray[T]) { - a.book = make([]ArrayPage[T], initialBookSize) + a.book = make([]*ArrayPage[T], initialBookSize) + for i := 0; i < initialBookSize; i++ { + a.book[i] = &ArrayPage[T]{} + } a.edpTasks = make([]EachDataTask[T], initialBookSize) a.edvpTasks = make([]EachDataValueTask[T], initialBookSize) - return a } @@ -26,7 +28,7 @@ type SlicePage[T any] struct { } type PagedArray[T any] struct { - book []ArrayPage[T] + book []*ArrayPage[T] currentPageIndex int len int wg sync.WaitGroup @@ -50,7 +52,7 @@ func (a *PagedArray[T]) Get(index int) *T { assert.True(index < a.len, "index out of range") pageId, index := a.getPageIdAndIndex(index) - page := &a.book[pageId] + page := a.book[pageId] return &(page.data[index]) } @@ -60,9 +62,9 @@ func (a *PagedArray[T]) GetValue(index int) T { assert.True(index < a.len, "index out of range") pageId, index := a.getPageIdAndIndex(index) - page := &a.book[pageId] + page := a.book[pageId] - return (page.data[index]) + return page.data[index] } func (a *PagedArray[T]) Set(index int, value T) *T { @@ -70,7 +72,7 @@ func (a *PagedArray[T]) Set(index int, value T) *T { assert.True(index < a.len, "index out of range") pageId, index := a.getPageIdAndIndex(index) - page := &a.book[pageId] + page := a.book[pageId] page.data[index] = value @@ -78,12 +80,22 @@ func (a *PagedArray[T]) Set(index int, value T) *T { } func (a *PagedArray[T]) extend() { - newBooks := make([]ArrayPage[T], len(a.book)*2) - a.book = append(a.book, newBooks...) - newEdvpTasks := make([]EachDataValueTask[T], len(a.edvpTasks)*2) - a.edvpTasks = append(a.edvpTasks, newEdvpTasks...) - newEdpTasks := make([]EachDataTask[T], len(a.edpTasks)*2) - a.edpTasks = append(a.edpTasks, newEdpTasks...) + oldLen := len(a.book) + newLen := oldLen * 2 + newBooks := make([]*ArrayPage[T], newLen) + copy(newBooks, a.book) + for i := oldLen; i < newLen; i++ { + newBooks[i] = &ArrayPage[T]{} + } + a.book = newBooks + + newEdvpTasks := make([]EachDataValueTask[T], newLen) + copy(newEdvpTasks, a.edvpTasks) + a.edvpTasks = newEdvpTasks + + newEdpTasks := make([]EachDataTask[T], newLen) + copy(newEdpTasks, a.edpTasks) + a.edpTasks = newEdpTasks } func (a *PagedArray[T]) Append(value T) *T { @@ -92,14 +104,14 @@ func (a *PagedArray[T]) Append(value T) *T { a.extend() } - page := &a.book[a.currentPageIndex] + page := a.book[a.currentPageIndex] if page.len == pageSize { a.currentPageIndex++ if a.currentPageIndex >= len(a.book) { a.extend() } - page = &a.book[a.currentPageIndex] + page = a.book[a.currentPageIndex] } page.data[page.len] = value result = &page.data[page.len] @@ -116,14 +128,14 @@ func (a *PagedArray[T]) AppendMany(values ...T) *T { a.extend() } - page := &a.book[a.currentPageIndex] + page := a.book[a.currentPageIndex] if page.len == pageSize { a.currentPageIndex++ if a.currentPageIndex >= len(a.book) { a.extend() } - page = &a.book[a.currentPageIndex] + page = a.book[a.currentPageIndex] } page.data[page.len] = value result = &page.data[page.len] @@ -138,7 +150,7 @@ func (a *PagedArray[T]) AppendMany(values ...T) *T { func (a *PagedArray[T]) SoftReduce() { assert.True(a.len > 0, "Len is already 0") - page := &a.book[a.currentPageIndex] + page := a.book[a.currentPageIndex] assert.True(page.len > 0, "Len is already 0") page.len-- @@ -152,7 +164,7 @@ func (a *PagedArray[T]) SoftReduce() { // Reset - resets the array to its initial state func (a *PagedArray[T]) Reset() { for i := 0; i <= a.currentPageIndex; i++ { - page := &a.book[i] + page := a.book[i] page.len = 0 } @@ -203,7 +215,7 @@ func (a *PagedArray[T]) Raw(result []T) []T { pos := 0 for i := 0; i <= a.currentPageIndex; i++ { - page := &a.book[i] + page := a.book[i] n := copy(result[pos:], page.data[:page.len]) pos += n } @@ -212,7 +224,7 @@ func (a *PagedArray[T]) Raw(result []T) []T { } func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { - return index >> pageSizeShift, index % pageSize + return index >> pageSizeShift, index & pageSizeMask } // ========================= @@ -222,7 +234,7 @@ func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { func (a *PagedArray[T]) Each() func(yield func(int, *T) bool) { return func(yield func(int, *T) bool) { var page *ArrayPage[T] - var index_offset int + var indexOffset int book := a.book @@ -231,11 +243,11 @@ func (a *PagedArray[T]) Each() func(yield func(int, *T) bool) { } for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] - index_offset = i << pageSizeShift + page = book[i] + indexOffset = i << pageSizeShift for j := page.len - 1; j >= 0; j-- { - if !yield(index_offset+j, &page.data[j]) { + if !yield(indexOffset+j, &page.data[j]) { return } } @@ -280,7 +292,7 @@ func (a *PagedArray[T]) EachData() func(yield func(*T) bool) { } for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] + page = book[i] for j := page.len - 1; j >= 0; j-- { if !yield(&page.data[j]) { @@ -301,7 +313,7 @@ func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { } for i := a.currentPageIndex; i >= 0; i-- { - page = &book[i] + page = book[i] for j := page.len - 1; j >= 0; j-- { if !yield(page.data[j]) { @@ -318,7 +330,7 @@ func (a *PagedArray[T]) ProcessDataValue(handler func(T, worker.WorkerId), pool pool.GroupAdd(a.currentPageIndex + 1) for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i - a.edvpTasks[j].page = &a.book[i] + a.edvpTasks[j].page = a.book[i] a.edvpTasks[j].f = handler pool.ProcessGroupTask(&a.edvpTasks[j]) } @@ -331,7 +343,7 @@ func (a *PagedArray[T]) EachDataParallel(handler func(*T, worker.WorkerId), pool pool.GroupAdd(a.currentPageIndex + 1) for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i - a.edpTasks[j].page = &a.book[i] + a.edpTasks[j].page = a.book[i] a.edpTasks[j].f = handler pool.ProcessGroupTask(&a.edpTasks[j]) } diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index dc190478..97830116 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -9,6 +9,7 @@ package ecs const ( pageSizeShift = 10 pageSize = 1 << pageSizeShift + pageSizeMask = pageSize - 1 initialBookSize = 1 // Starting with a small initial book size ) From 3a43bf949b4cc40bd4d1741047a7fe485fc83724 Mon Sep 17 00:00:00 2001 From: bitver Date: Fri, 2 May 2025 23:55:21 +0500 Subject: [PATCH 176/196] fps --- examples/new-api/systems/render-overlay.go | 241 ++++++++++++++++++++- pkg/ecs/paged-map.go | 2 +- 2 files changed, 236 insertions(+), 7 deletions(-) diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index 6ef0dd78..a7204911 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -27,6 +27,14 @@ import ( "time" ) +const ( + fpsAvgSamples = 100 + fontSize = 20 + fpsGraphWidth = 160 + fpsGraphHeight = 60 + msGraphMaxValue = 33.33 // Max ms to show on graph (30 FPS) +) + func NewRenderOverlaySystem() RenderOverlaySystem { return RenderOverlaySystem{} } @@ -50,6 +58,17 @@ type RenderOverlaySystem struct { monitorHeight int debugLvl int debug bool + lastFPSTime time.Time + frameCount int + currentFPS int + fpsSamples []int + fpsSampleSum int + fpsSampleIdx int + avgFPS float64 + percentileFPS int + lastFrameDuration time.Duration + msHistory []float64 // ms per frame, ring buffer + msHistoryIdx int } func (s *RenderOverlaySystem) Init() { @@ -65,6 +84,9 @@ func (s *RenderOverlaySystem) Init() { Tint: rl.White, Dst: rl.Rectangle{Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)}, }) + + // Initialize ms history buffer for graph + s.msHistory = make([]float64, fpsGraphWidth) } func (s *RenderOverlaySystem) Run(dt time.Duration) bool { @@ -88,6 +110,48 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } } + // FPS calculation (custom) + now := time.Now() + if s.lastFPSTime.IsZero() { + s.lastFPSTime = now + s.fpsSamples = make([]int, fpsAvgSamples) + s.msHistory = make([]float64, fpsGraphWidth) + } + s.frameCount++ + s.lastFrameDuration = dt + + // Store current frame FPS in samples + frameFPS := 0 + if dt > 0 { + // Correct calculation: convert duration to frames per second + frameFPS = int(time.Second / dt) + } + s.fpsSampleSum -= s.fpsSamples[s.fpsSampleIdx] + s.fpsSamples[s.fpsSampleIdx] = frameFPS + s.fpsSampleSum += frameFPS + s.fpsSampleIdx = (s.fpsSampleIdx + 1) % len(s.fpsSamples) + + // Calculate average FPS over samples + s.avgFPS = float64(s.fpsSampleSum) / float64(len(s.fpsSamples)) + + // Calculate 1% FPS (lowest 1% frame in the sample window) + s.percentileFPS = s.calcPercentileFPS(0.01) + + // Update frame time history (ms) on every frame + // Use average of last two frames for smoother graph + var ms float64 + if s.lastFrameDuration > 0 { + ms = float64(s.lastFrameDuration.Microseconds()) / 1000.0 + s.msHistory[s.msHistoryIdx] = ms + s.msHistoryIdx = (s.msHistoryIdx + 1) % len(s.msHistory) + } + + if now.Sub(s.lastFPSTime) >= time.Second { + s.currentFPS = s.frameCount + s.frameCount = 0 + s.lastFPSTime = now + } + s.Cameras.EachEntity()(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) frame := s.FrameBuffer2D.GetUnsafe(entity) @@ -233,14 +297,18 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } // Print stats - rl.DrawRectangleRec(rl.Rectangle{Height: 120, Width: 200}, rl.Black) - rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 70, 20, rl.RayWhite) - rl.DrawText(fmt.Sprintf("%d debugLvl", s.debugLvl), 10, 90, 20, rl.RayWhite) + const x = 10 + const y = 10 + statsPanelWidth := float32(200 + fpsGraphWidth) + statsPanelHeight := float32(y + fontSize*8) + rl.DrawRectangleRec(rl.Rectangle{Height: statsPanelHeight, Width: statsPanelWidth}, rl.Black) + s.drawCustomFPS(x, y) + rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), x, y+fontSize*6, fontSize, rl.RayWhite) + rl.DrawText(fmt.Sprintf("%d debugLvl", s.debugLvl), x, y+fontSize*7, 20, rl.RayWhite) // Game over s.SceneManager.EachComponent()(func(a *components.AsteroidSceneManager) bool { - rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), 10, 30, 20, rl.RayWhite) - rl.DrawText(fmt.Sprintf("Score: %d", a.PlayerScore), 10, 50, 20, rl.RayWhite) + rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), x, y+fontSize*4, 20, rl.RayWhite) + rl.DrawText(fmt.Sprintf("Score: %d", a.PlayerScore), x, y+fontSize*5, 20, rl.RayWhite) if a.PlayerHp <= 0 { text := "Game Over" textSize := rl.MeasureTextEx(rl.GetFontDefault(), text, 96, 0) @@ -264,6 +332,167 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { return true } +// Draws FPS stats: 1% low, current frame, average, and current FPS +func (s *RenderOverlaySystem) drawCustomFPS(x, y int32) { + fps := int32(s.currentFPS) + + // Frame time in milliseconds + frameTimeMs := 0.0 + if s.lastFrameDuration > 0 { + frameTimeMs = float64(s.lastFrameDuration.Microseconds()) / 1000.0 + } + + avgFPS := int32(s.avgFPS) + percentileFPS := int32(s.percentileFPS) + + // Colors + fontColor := rl.Lime + if fps < 30 { + fontColor = rl.Red + } else if fps < 60 { + fontColor = rl.Yellow + } + + // Frame time color (lower is better) + frameTimeColor := rl.Lime + if frameTimeMs > 33.33 { // 30 FPS threshold (33.33ms) + frameTimeColor = rl.Red + } else if frameTimeMs > 16.67 { // 60 FPS threshold (16.67ms) + frameTimeColor = rl.Yellow + } + + // Draw all stats + rl.DrawText(fmt.Sprintf("FPS: %d", fps), x, y, fontSize, fontColor) + rl.DrawText(fmt.Sprintf("Frame: %.2f ms", frameTimeMs), x, y+fontSize, fontSize, frameTimeColor) + rl.DrawText(fmt.Sprintf("Avg %d: %d", fpsAvgSamples, avgFPS), x, y+fontSize*2, fontSize, fontColor) + rl.DrawText(fmt.Sprintf("1%% Low: %d", percentileFPS), x, y+fontSize*3, fontSize, fontColor) + + // Draw ms graph + s.drawMsGraph(x+180, y) +} + +// Draw a graph of historical frame times in milliseconds +func (s *RenderOverlaySystem) drawMsGraph(x, y int32) { + // Draw graph border + rl.DrawRectangleLinesEx(rl.Rectangle{ + X: float32(x), + Y: float32(y), + Width: float32(fpsGraphWidth), + Height: float32(fpsGraphHeight), + }, 1, rl.Gray) + + // Draw graph background + rl.DrawRectangle(x+1, y+1, fpsGraphWidth-2, fpsGraphHeight-2, rl.Black) + + // Draw horizontal reference lines (33.33ms, 16.67ms, 8.33ms) + // These correspond to 30 FPS, 60 FPS, and 120 FPS + refLines := []struct { + ms float32 + color rl.Color + label string + }{ + {33.33, rl.Red, "33.33 (30 FPS)"}, + {16.67, rl.Yellow, "16.67 (60 FPS)"}, + {8.33, rl.Green, "8.33 (120 FPS)"}, + } + for _, ref := range refLines { + refY := y + int32(float32(fpsGraphHeight)*(ref.ms/msGraphMaxValue)) + rl.DrawLineEx( + rl.NewVector2(float32(x), float32(refY)), + rl.NewVector2(float32(x+int32(fpsGraphWidth)), float32(refY)), + 1.0, + ref.color, + ) + rl.DrawText(ref.label, x+int32(fpsGraphWidth)+2, refY-8, 10, rl.Fade(ref.color, 0.8)) + } + + // Start from the oldest sample and move forward + startIdx := s.msHistoryIdx % len(s.msHistory) + + // Draw frame time data points and connect with lines + for i := 0; i < len(s.msHistory)-1; i++ { + // Calculate indices in a way that we're drawing from left to right, + // with the newest data on the right + idx := (startIdx + i) % len(s.msHistory) + nextIdx := (startIdx + i + 1) % len(s.msHistory) + + ms1 := float32(s.msHistory[idx]) + ms2 := float32(s.msHistory[nextIdx]) + + // Clamp values to max + if ms1 > msGraphMaxValue { + ms1 = msGraphMaxValue + } + if ms2 > msGraphMaxValue { + ms2 = msGraphMaxValue + } + + // Calculate positions (note: for ms, higher value = worse performance, so we scale directly) + x1 := x + int32(i) + y1 := y + int32(float32(fpsGraphHeight)*(ms1/msGraphMaxValue)) + x2 := x + int32(i+1) + y2 := y + int32(float32(fpsGraphHeight)*(ms2/msGraphMaxValue)) + + // Choose color based on frame time + lineColor := rl.Green + if ms2 > 16.67 { // 60 FPS threshold + lineColor = rl.Yellow + } + if ms2 > 33.33 { // 30 FPS threshold + lineColor = rl.Red + } + + // Skip drawing if either value is zero (not yet initialized) + if ms1 > 0 && ms2 > 0 { + rl.DrawLineEx( + rl.NewVector2(float32(x1), float32(y1)), + rl.NewVector2(float32(x2), float32(y2)), + 2.0, + lineColor, + ) + } + } + + // Draw a vertical line indicating the current position in the buffer + currentX := x + int32(len(s.msHistory)-1) + rl.DrawLineEx( + rl.NewVector2(float32(currentX), float32(y)), + rl.NewVector2(float32(currentX), float32(y+int32(fpsGraphHeight))), + 1.0, + rl.White, + ) +} + +// Calculates the given percentile FPS (e.g., 0.01 for 1% low) +func (s *RenderOverlaySystem) calcPercentileFPS(percentile float64) int { + n := len(s.fpsSamples) + if n == 0 { + return 0 + } + // Copy and sort samples + sorted := make([]int, n) + copy(sorted, s.fpsSamples) + for i := 1; i < n; i++ { + key := sorted[i] + j := i - 1 + for j >= 0 && sorted[j] > key { + sorted[j+1] = sorted[j] + j-- + } + sorted[j+1] = key + } + + // For 1% low, we want the 1st percentile (lowest values) + idx := int(float64(n) * percentile) + if idx < 0 { + idx = 0 + } + if idx >= n { + idx = n - 1 + } + return sorted[idx] +} + func (s *RenderOverlaySystem) intersects(rect1, rect2 vectors.Rectangle) bool { return rect1.X < rect2.X+rect2.Width && rect1.X+rect1.Width > rect2.X && diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 97830116..1e02af51 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -88,7 +88,7 @@ func (m *PagedMap[K, V]) Has(key K) bool { } func (m *PagedMap[K, V]) getPageIDAndIndex(key K) (pageID int, index int) { - return int(uint64(key) >> pageSizeShift), int(uint64(key) % pageSize) + return int(key) >> pageSizeShift, int(key) & pageSizeMask } func (m *PagedMap[K, V]) expandBook(minLen int) { From a49106695b8f1105601b75e446709aa1f23e1286 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 3 May 2025 16:05:20 +0300 Subject: [PATCH 177/196] feat collision-setup.go --- examples/new-api/game.go | 2 +- examples/new-api/instances/component-list.go | 6 +- examples/new-api/systems/asterodd.go | 13 + examples/new-api/systems/camera-main.go | 2 +- examples/new-api/systems/render-overlay.go | 16 - pkg/ecs/gmap.go | 104 +++++ pkg/ecs/gmap_test.go | 164 ++++++++ pkg/worker/worker.go | 3 - stdcomponents/collision-cell.go | 99 +++-- stdcomponents/collision-grid-member.go | 27 ++ stdcomponents/collision-grid.go | 84 ++-- stdcomponents/ids.go | 4 +- stdcomponents/spatial-index.go | 16 +- stdentities/collision-grid.go | 37 ++ stdsystems/animation-player.go | 53 ++- stdsystems/collision-detection-bvh.go | 2 +- stdsystems/collision-detection-grid.go | 368 +++++++++-------- stdsystems/collision-detection.go | 57 ++- stdsystems/collision-setup.go | 410 ++++++++++--------- stdsystems/debug.go | 7 +- taskfile.yml | 8 +- 21 files changed, 934 insertions(+), 548 deletions(-) create mode 100644 pkg/ecs/gmap.go create mode 100644 pkg/ecs/gmap_test.go create mode 100644 stdcomponents/collision-grid-member.go create mode 100644 stdentities/collision-grid.go diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 4340658d..9596e2a5 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -106,7 +106,7 @@ func (g *Game) FixedUpdate(dt time.Duration) { systems.Velocity.Run(dt) systems.ColliderSystem.Run(dt) systems.CollisionSetup.Run(dt) - systems.CollisionDetection.Run(dt) + //systems.CollisionDetection.Run(dt) //systems.CollisionDetectionBVH.Run(dt) systems.CollisionResolution.Run(dt) diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index f75906ff..e2a39fdd 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -41,7 +41,7 @@ type ComponentList struct { ColliderSleepState stdcomponents.ColliderSleepStateComponentManager Collision stdcomponents.CollisionComponentManager AABB stdcomponents.AABBComponentManager - SpatialIndex stdcomponents.SpatialIndexComponentManager + SpatialIndex stdcomponents.SpatialHashComponentManager RigidBody stdcomponents.RigidBodyComponentManager BvhTree stdcomponents.BvhTreeComponentManager Cameras stdcomponents.CameraComponentManager @@ -50,6 +50,7 @@ type ComponentList struct { CollisionGrid stdcomponents.CollisionGridComponentManager CollisionChunk stdcomponents.CollisionChunkComponentManager CollisionCell stdcomponents.CollisionCellComponentManager + CollisionGridMember stdcomponents.CollisionGridMemberComponentManager Health components.HpComponentManager Controller components.ControllerComponentManager @@ -91,7 +92,7 @@ func NewComponentList() ComponentList { ColliderSleepState: stdcomponents.NewColliderSleepStateComponentManager(), Collision: stdcomponents.NewCollisionComponentManager(), AABB: stdcomponents.NewAABBComponentManager(), - SpatialIndex: stdcomponents.NewSpatialIndexComponentManager(), + SpatialIndex: stdcomponents.NewSpatialHashComponentManager(), RigidBody: stdcomponents.NewRigidBodyComponentManager(), BvhTree: stdcomponents.NewBvhTreeComponentManager(), Cameras: stdcomponents.NewCameraComponentManager(), @@ -100,6 +101,7 @@ func NewComponentList() ComponentList { CollisionGrid: stdcomponents.NewCollisionGridComponentManager(), CollisionChunk: stdcomponents.NewCollisionChunkComponentManager(), CollisionCell: stdcomponents.NewCollisionCellComponentManager(), + CollisionGridMember: stdcomponents.NewCollisionGridMemberComponentManager(), Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index d45f98f2..ed649ff3 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -17,9 +17,11 @@ package systems import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/examples/new-api/components" + "gomp/examples/new-api/config" "gomp/examples/new-api/entities" "gomp/pkg/ecs" "gomp/stdcomponents" + "gomp/stdentities" "gomp/vectors" "math/rand" "time" @@ -43,6 +45,7 @@ type AssteroddSystem struct { TexturePositionSmooth *stdcomponents.TexturePositionSmoothComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager Textures *stdcomponents.RLTextureProComponentManager + CollisionGrids *stdcomponents.CollisionGridComponentManager PlayerTags *components.PlayerTagComponentManager AsteroidTags *components.AsteroidComponentManager @@ -58,6 +61,16 @@ type AssteroddSystem struct { } func (s *AssteroddSystem) Init() { + collisionGridManages := stdentities.CreateCollisionGridManagers{ + EntityManager: s.EntityManager, + Grid: s.CollisionGrids, + } + stdentities.CreateCollisionGrid(&collisionGridManages, config.DefaultCollisionLayer, 256) + stdentities.CreateCollisionGrid(&collisionGridManages, config.PlayerCollisionLayer, 128) + stdentities.CreateCollisionGrid(&collisionGridManages, config.BulletCollisionLayer, 32) + stdentities.CreateCollisionGrid(&collisionGridManages, config.EnemyCollisionLayer, 128) + stdentities.CreateCollisionGrid(&collisionGridManages, config.WallCollisionLayer, 4096) + entities.CreateSpaceShip(entities.CreateSpaceShipManagers{ EntityManager: s.EntityManager, Positions: s.Positions, diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index 7f4f9519..df59d3dd 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -85,7 +85,7 @@ func (s *MainCameraSystem) Run(dt time.Duration) { scroll := rl.GetMouseWheelMove() if scroll != 0.0 { c := s.Cameras.GetUnsafe(s.mainCamera) - c.Zoom += scroll * float32(dt.Seconds()) + c.Zoom += scroll * 0.1 if c.Zoom < 0.1 { c.Zoom = 0.1 } diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index a7204911..6c4ea610 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -180,22 +180,6 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { position := s.Positions.GetUnsafe(e) assert.NotNil(position) - tree := s.BvhTrees.GetUnsafe(e) - assert.NotNil(tree) - - tree.AabbNodes.EachData()(func(a *stdcomponents.AABB) bool { - // Simple AABB culling - if s.intersects(cameraRect, a.Rect()) { - rl.DrawRectangleRec(rl.Rectangle{ - X: a.Min.X, - Y: a.Min.Y, - Width: a.Max.X - a.Min.X, - Height: a.Max.Y - a.Min.Y, - }, *tint) - } - return true - }) - clr := color.RGBA{ R: tint.R, G: tint.G, diff --git a/pkg/ecs/gmap.go b/pkg/ecs/gmap.go new file mode 100644 index 00000000..f0fe9f2a --- /dev/null +++ b/pkg/ecs/gmap.go @@ -0,0 +1,104 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "iter" + + "github.com/negrel/assert" +) + +func NewGenMap[K comparable, V any](cap int) GenMap[K, V] { + return GenMap[K, V]{ + data: make(map[K]genMapValue[V], cap), + generation: 1, + } +} + +type genMapValue[V any] struct { + generation int32 + value V +} + +type GenMap[K comparable, V any] struct { + generation int32 + data map[K]genMapValue[V] + len int +} + +func (m *GenMap[K, V]) Get(key K) (V, bool) { + v, ok := m.data[key] + if !ok { + return v.value, ok + } + if v.generation != m.generation { + return v.value, false + } + return v.value, ok +} + +func (m *GenMap[K, V]) Set(key K, value V) { + if !m.Has(key) { + m.len++ + } + m.data[key] = genMapValue[V]{ + generation: m.generation, + value: value, + } +} + +func (m *GenMap[K, V]) Reset() { + m.generation++ + m.len = 0 +} + +func (m *GenMap[K, V]) Delete(key K) { + _, ok := m.data[key] + assert.True(ok) + m.data[key] = genMapValue[V]{ + generation: m.generation - 1, + } +} + +func (m *GenMap[K, V]) Has(key K) bool { + v, ok := m.data[key] + return ok && v.generation == m.generation +} + +func (m *GenMap[K, V]) Len() int { + return m.len +} + +func (m *GenMap[K, V]) Each() iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + for k, v := range m.data { + if v.generation != m.generation { + continue + } + if !yield(k, v.value) { + return + } + } + } +} + +func (m *GenMap[K, V]) Clear() { + for i, v := range m.data { + if v.generation == m.generation { + continue + } + delete(m.data, i) + } +} diff --git a/pkg/ecs/gmap_test.go b/pkg/ecs/gmap_test.go new file mode 100644 index 00000000..54672a8c --- /dev/null +++ b/pkg/ecs/gmap_test.go @@ -0,0 +1,164 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGenMap(t *testing.T) { + t.Run("NewGenMap", func(t *testing.T) { + m := NewGenMap[string, int](10) + assert.Equal(t, m.Len(), 0) + }) + + t.Run("Set/Get/Has", func(t *testing.T) { + m := NewGenMap[string, int](0) + + // Проверка отсутствующего ключа + _, ok := m.Get("missing") + assert.False(t, ok) + assert.False(t, m.Has("missing")) + + // Добавление нового ключа + m.Set("a", 1) + val, ok := m.Get("a") + assert.True(t, ok) + assert.Equal(t, val, 1) + assert.True(t, m.Has("a")) + + // Обновление существующего ключа + m.Set("a", 42) + val, _ = m.Get("a") + assert.Equal(t, val, 42) + }) + + t.Run("Reset", func(t *testing.T) { + m := NewGenMap[string, int](0) + m.Set("a", 1) + m.Set("b", 2) + + // Сброс поколения + m.Reset() + + // Ключи не должны быть доступны + assert.False(t, m.Has("a")) + assert.False(t, m.Has("b")) + assert.Equal(t, m.Len(), 0) + + // Добавление новых ключей после сброса + m.Set("c", 3) + assert.True(t, m.Has("c")) + assert.Equal(t, m.Len(), 1) + }) + + t.Run("Delete", func(t *testing.T) { + m := NewGenMap[string, int](0) + m.Set("a", 1) + + // Удаление существующего ключа + m.Delete("a") + assert.False(t, m.Has("a")) + + // Повторное добавление после удаления + m.Set("a", 2) + assert.True(t, m.Has("a")) + val, _ := m.Get("a") + assert.Equal(t, val, 2) + }) + + t.Run("Len", func(t *testing.T) { + m := NewGenMap[string, int](0) + assert.Equal(t, m.Len(), 0) + + m.Set("a", 1) + m.Set("b", 2) + assert.Equal(t, m.Len(), 2) + + m.Reset() + assert.Equal(t, m.Len(), 0) + }) + + t.Run("Each", func(t *testing.T) { + m := NewGenMap[string, int](0) + m.Set("a", 1) + m.Set("b", 2) + + // Проверка итерации + count := 0 + for k, v := range m.Each() { + count++ + assert.True(t, k == "a" || k == "b") + assert.True(t, v == 1 || v == 2) + } + assert.Equal(t, count, 2) + + // После сброса итерация пуста + m.Reset() + count = 0 + for range m.Each() { + count++ + } + assert.Equal(t, count, 0) + }) + + t.Run("Clear", func(t *testing.T) { + m := NewGenMap[string, int](0) + m.Set("a", 1) + m.Reset() + m.Set("b", 2) + + // Очистка старых поколений + m.Clear() + + // Проверка, что старые записи удалены + assert.Equal(t, len(m.data), 1) + assert.True(t, m.Has("b")) + }) + + t.Run("MultipleGenerations", func(t *testing.T) { + m := NewGenMap[string, int](0) + + // Generation 0 + m.Set("a", 1) + m.Set("b", 2) + + // Generation 1 + m.Reset() + m.Set("b", 20) + m.Set("c", 30) + + // Проверка наличия ключей + assert.False(t, m.Has("a")) // Из generation 0 + assert.True(t, m.Has("b")) // Обновлено в generation 1 + assert.True(t, m.Has("c")) + + // Проверка значений + val, _ := m.Get("b") + assert.Equal(t, val, 20) + }) + + t.Run("ReuseKeyAfterReset", func(t *testing.T) { + m := NewGenMap[string, int](0) + m.Set("a", 1) + m.Reset() + m.Set("a", 2) // Тот же ключ, новое поколение + + assert.True(t, m.Has("a")) + val, _ := m.Get("a") + assert.Equal(t, val, 2) + }) +} diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go index fb47adfd..01459ab0 100644 --- a/pkg/worker/worker.go +++ b/pkg/worker/worker.go @@ -16,7 +16,6 @@ package worker import ( "context" - "runtime" "sync" ) @@ -48,8 +47,6 @@ func (w *Worker) Stop() { } func (w *Worker) run(poolWg *sync.WaitGroup) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() defer poolWg.Done() for { select { diff --git a/stdcomponents/collision-cell.go b/stdcomponents/collision-cell.go index 51619f09..7d950278 100644 --- a/stdcomponents/collision-cell.go +++ b/stdcomponents/collision-cell.go @@ -17,40 +17,83 @@ package stdcomponents import ( "gomp/pkg/ecs" "gomp/pkg/worker" + "sync" + + "github.com/negrel/assert" ) -type CollisionCell struct { - //Members ecs.PagedArray[ecs.Entity] - //MemberLookup ecs.PagedMap[ecs.Entity, int] +const ( + MembersPerCellSqrt = 2 + membersPerCell = MembersPerCellSqrt * MembersPerCellSqrt +) + +func NewMemberListPool(workerPool *worker.Pool) MemberListPool { + return MemberListPool{ + pool: sync.Pool{ + New: func() any { + return &MemberList{ + Members: make([]ecs.Entity, 0, membersPerCell), + Lookup: ecs.NewGenMap[ecs.Entity, int](membersPerCell), + InputAcc: make([][]ecs.Entity, workerPool.NumWorkers()), + } + }, + }, + } +} + +type MemberListPool struct { + pool sync.Pool +} + +func (p *MemberListPool) Get() *MemberList { + return p.pool.Get().(*MemberList) +} + +func (p *MemberListPool) Put(ml *MemberList) { + p.pool.Put(ml) +} + +type MemberList struct { + Members []ecs.Entity + Lookup ecs.GenMap[ecs.Entity, int] + InputAcc [][]ecs.Entity +} - InputAccumulator []ecs.PagedArray[ecs.Entity] - Size float32 - Layer CollisionLayer +func (ml *MemberList) Add(member ecs.Entity) { + ml.Members = append(ml.Members, member) + ml.Lookup.Set(member, len(ml.Members)-1) } -func (c *CollisionCell) Init(size float32, layer CollisionLayer, pool *worker.Pool) { - //c.Members = ecs.NewPagedArray[ecs.Entity]() - //c.MemberLookup = ecs.NewPagedMap[ecs.Entity, int]() - c.InputAccumulator = make([]ecs.PagedArray[ecs.Entity], pool.NumWorkers()) - for i := 0; i < pool.NumWorkers(); i++ { - c.InputAccumulator[i] = ecs.NewPagedArray[ecs.Entity]() +func (ml *MemberList) Delete(member ecs.Entity) { + index, ok := ml.Lookup.Get(member) + assert.True(ok) + lastIndex := len(ml.Members) - 1 + if index < lastIndex { + // Swap the dead element with the last one + ml.Members[index], ml.Members[lastIndex] = ml.Members[lastIndex], ml.Members[index] + // Update Lookup table + ml.Lookup.Set(ml.Members[index], index) } - c.Size = size - c.Layer = layer -} - -//func (c *CollisionCell) AddMember(entity ecs.Entity) { -// c.Members.Append(entity) -// c.MemberLookup.Set(entity, c.Members.Len()-1) -//} -// -//func (c *CollisionCell) RemoveMember(entity ecs.Entity) { -// index, ok := c.MemberLookup.Get(entity) -// assert.True(ok) -// c.Members.Swap(index, c.Members.Len()-1) -// c.Members.SoftReduce() -// c.MemberLookup.Delete(entity) -//} + ml.Members = ml.Members[:lastIndex] + ml.Lookup.Delete(member) +} + +func (ml *MemberList) Reset() { + ml.Lookup.Reset() + ml.Members = ml.Members[:0] +} + +func (ml *MemberList) Has(member ecs.Entity) bool { + return ml.Lookup.Has(member) +} + +type CollisionCell struct { + Index SpatialCellIndex + Layer CollisionLayer + Grid ecs.Entity + Size float32 + Members *MemberList +} type CollisionCellComponentManager = ecs.ComponentManager[CollisionCell] diff --git a/stdcomponents/collision-grid-member.go b/stdcomponents/collision-grid-member.go new file mode 100644 index 00000000..9f808150 --- /dev/null +++ b/stdcomponents/collision-grid-member.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type CollisionGridMember struct { + Grid ecs.Entity +} + +type CollisionGridMemberComponentManager = ecs.ComponentManager[CollisionGridMember] + +func NewCollisionGridMemberComponentManager() CollisionGridMemberComponentManager { + return ecs.NewComponentManager[CollisionGridMember](CollisionGridMemberComponentId) +} diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go index 1fe921ee..bb501184 100644 --- a/stdcomponents/collision-grid.go +++ b/stdcomponents/collision-grid.go @@ -7,7 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- tema881 Donated 100 RUB Thank you for your support! */ @@ -16,75 +16,67 @@ package stdcomponents import ( "gomp/pkg/ecs" - "gomp/pkg/worker" "gomp/vectors" - "math" ) +func NewCollisionGrid(collisionLayer CollisionLayer, cellSize float32) CollisionGrid { + g := CollisionGrid{ + Layer: collisionLayer, + CellSize: cellSize, + CellMap: ecs.NewGenMap[SpatialCellIndex, ecs.Entity](1024), + CreateCellsAccumulator: nil, + } + + return g +} + type CollisionGrid struct { - Entities ecs.PagedArray[ecs.Entity] // List of Entities in the grid - ChunkLookup map[SpatialIndex]ecs.Entity // Pointer to cell - ChunkSize float32 - MinBounds vectors.Vec2 - - // NEW API - Layer CollisionLayer // Layer of the grid - Cells ecs.PagedArray[ecs.Entity] // List of Cells in the grid - CellLookup map[SpatialIndex]int // Index to cell in Cells - CellSizeAccumulator []float32 // Accumulator for cell size - CellAccumulator []map[SpatialIndex]struct{} // Accumulator for Cells - CellSize float32 // Size of a cell + Layer CollisionLayer // Layer of the grid + CellSize float32 // Size of a cell + + CreateCellsAccumulator []ecs.GenMap[SpatialCellIndex, struct{}] + CellMap ecs.GenMap[SpatialCellIndex, ecs.Entity] } -func (g *CollisionGrid) Init(collisionLayer CollisionLayer, pool *worker.Pool) { - g.Layer = collisionLayer - g.CellSize = math.MaxFloat32 - g.Cells = ecs.NewPagedArray[ecs.Entity]() - g.CellLookup = make(map[SpatialIndex]int) - g.CellSizeAccumulator = make([]float32, pool.NumWorkers()) - g.CellAccumulator = make([]map[SpatialIndex]struct{}, pool.NumWorkers()) - for i := 0; i < pool.NumWorkers(); i++ { - g.CellSizeAccumulator[i] = math.MaxFloat32 - g.CellAccumulator[i] = make(map[SpatialIndex]struct{}) - } +type SpatialCellIndex struct { + X, Y int +} + +func (i SpatialCellIndex) ToVec2() vectors.Vec2 { + return vectors.Vec2{X: float32(i.X), Y: float32(i.Y)} } // Query returns the EntityIds of Cells that intersect the AABB func (g *CollisionGrid) Query(bb AABB, result []ecs.Entity) []ecs.Entity { // get spatial index of aabb - minSpatialIndex := g.GetSpatialIndex(bb.Min) - maxSpatialIndex := g.GetSpatialIndex(bb.Max) + minSpatialCellIndex := g.GetCellIndex(bb.Min) + maxSpatialCellIndex := g.GetCellIndex(bb.Max) // make a list of all spatial indexes that intersect the aabb // get cells that intersect the aabb by spatial indexes - for i := minSpatialIndex.X; i <= maxSpatialIndex.X; i++ { - for j := minSpatialIndex.Y; j <= maxSpatialIndex.Y; j++ { - spatialIndex := SpatialIndex{X: i, Y: j} - cellIndex, exists := g.CellLookup[spatialIndex] + for i := minSpatialCellIndex.X; i <= maxSpatialCellIndex.X; i++ { + for j := minSpatialCellIndex.Y; j <= maxSpatialCellIndex.Y; j++ { + spatialIndex := SpatialCellIndex{X: i, Y: j} + cellEntity, exists := g.CellMap.Get(spatialIndex) if !exists { continue - } - cell := g.Cells.GetValue(cellIndex) - result = append(result, cell) + result = append(result, cellEntity) } } return result } -func (g *CollisionGrid) RegisterEntity(entity ecs.Entity, aabb *AABB) { - g.Entities.Append(entity) - - l := aabb.Max.Sub(aabb.Min) - - if l.LengthSquared() < g.MinBounds.LengthSquared() { - g.MinBounds = l +func (g *CollisionGrid) GetCellIndex(position vectors.Vec2) SpatialCellIndex { + return SpatialCellIndex{ + X: int(position.X / g.CellSize), + Y: int(position.Y / g.CellSize), } } -func (g *CollisionGrid) GetSpatialIndex(position vectors.Vec2) SpatialIndex { - return SpatialIndex{ - X: int(position.X / g.CellSize), - Y: int(position.Y / g.CellSize), +func (g *CollisionGrid) CalculateSpatialHash(bb AABB) SpatialHash { + return SpatialHash{ + Min: g.GetCellIndex(bb.Min), + Max: g.GetCellIndex(bb.Max), } } diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 302d5d23..8184c516 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -36,13 +36,15 @@ const ( ColliderSleepStateComponentId PolygonColliderComponentId CollisionComponentId - SpatialIndexComponentId + SpatialHashComponentId AABBComponentId RigidBodyComponentId BvhTreeComponentId CollisionGridComponentId CollisionCellComponentId CollisionChunkComponentId + CollisionGridMemberComponentId + s FrameBuffer2DComponentId CameraComponentId TexturePositionSmoothComponentId diff --git a/stdcomponents/spatial-index.go b/stdcomponents/spatial-index.go index a918a306..601e4b27 100644 --- a/stdcomponents/spatial-index.go +++ b/stdcomponents/spatial-index.go @@ -16,19 +16,15 @@ package stdcomponents import ( "gomp/pkg/ecs" - "gomp/vectors" ) -type SpatialIndex struct { - X, Y int +type SpatialHash struct { + Min SpatialCellIndex + Max SpatialCellIndex } -func (i SpatialIndex) ToVec2() vectors.Vec2 { - return vectors.Vec2{X: float32(i.X), Y: float32(i.Y)} -} - -type SpatialIndexComponentManager = ecs.ComponentManager[SpatialIndex] +type SpatialHashComponentManager = ecs.ComponentManager[SpatialHash] -func NewSpatialIndexComponentManager() SpatialIndexComponentManager { - return ecs.NewComponentManager[SpatialIndex](SpatialIndexComponentId) +func NewSpatialHashComponentManager() SpatialHashComponentManager { + return ecs.NewComponentManager[SpatialHash](SpatialHashComponentId) } diff --git a/stdentities/collision-grid.go b/stdentities/collision-grid.go new file mode 100644 index 00000000..76af2170 --- /dev/null +++ b/stdentities/collision-grid.go @@ -0,0 +1,37 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdentities + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +type CreateCollisionGridManagers struct { + EntityManager *ecs.EntityManager + Grid *stdcomponents.CollisionGridComponentManager +} + +func CreateCollisionGrid( + props *CreateCollisionGridManagers, + layer stdcomponents.CollisionLayer, + cellSize float32, +) ecs.Entity { + e := props.EntityManager.Create() + + props.Grid.Create(e, stdcomponents.NewCollisionGrid(layer, cellSize)) + + return e +} diff --git a/stdsystems/animation-player.go b/stdsystems/animation-player.go index 7e04282e..8db224ae 100644 --- a/stdsystems/animation-player.go +++ b/stdsystems/animation-player.go @@ -8,6 +8,7 @@ package stdsystems import ( "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "time" @@ -28,43 +29,41 @@ func (s *AnimationPlayerSystem) Init() { } func (s *AnimationPlayerSystem) Run() { dt := time.Since(s.lastRunAt) - s.AnimationPlayers.EachComponent()( - func(animation *stdcomponents.AnimationPlayer) bool { - animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond + s.AnimationPlayers.ProcessComponents(func(animation *stdcomponents.AnimationPlayer, workerId worker.WorkerId) { + animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond - assert.True(animation.FrameDuration > 0, "frame duration must be greater than 0") + assert.True(animation.FrameDuration > 0, "frame duration must be greater than 0") - // Check if animation is playing backwards - if animation.Speed < 0 { - for animation.ElapsedTime <= 0 { - animation.ElapsedTime += animation.FrameDuration - animation.Current-- + // Check if animation is playing backwards + if animation.Speed < 0 { + for animation.ElapsedTime <= 0 { + animation.ElapsedTime += animation.FrameDuration + animation.Current-- - if animation.Current < animation.First { - if animation.Loop { - animation.Current = animation.Last - } else { - animation.Current = animation.First - } + if animation.Current < animation.First { + if animation.Loop { + animation.Current = animation.Last + } else { + animation.Current = animation.First } } - } else { - for animation.ElapsedTime >= animation.FrameDuration { - animation.ElapsedTime -= animation.FrameDuration - animation.Current++ + } + } else { + for animation.ElapsedTime >= animation.FrameDuration { + animation.ElapsedTime -= animation.FrameDuration + animation.Current++ - if animation.Current > animation.Last { - if animation.Loop { - animation.Current = animation.First - } else { - animation.Current = animation.Last - } + if animation.Current > animation.Last { + if animation.Loop { + animation.Current = animation.First + } else { + animation.Current = animation.Last } } } + } - return true - }) + }) s.lastRunAt = time.Now() } func (s *AnimationPlayerSystem) Destroy() {} diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 10cbfcff..1ceb07a2 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -46,7 +46,7 @@ type CollisionDetectionBVHSystem struct { CircleColliders *stdcomponents.CircleColliderComponentManager PolygonColliders *stdcomponents.PolygonColliderComponentManager Collisions *stdcomponents.CollisionComponentManager - SpatialIndex *stdcomponents.SpatialIndexComponentManager + SpatialIndex *stdcomponents.SpatialHashComponentManager AABB *stdcomponents.AABBComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager BvhTreeComponentManager *stdcomponents.BvhTreeComponentManager diff --git a/stdsystems/collision-detection-grid.go b/stdsystems/collision-detection-grid.go index 8f285aaf..58d26d36 100644 --- a/stdsystems/collision-detection-grid.go +++ b/stdsystems/collision-detection-grid.go @@ -19,8 +19,6 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" - "runtime" - "sync" "time" ) @@ -28,7 +26,7 @@ func NewCollisionDetectionGridSystem() CollisionDetectionGridSystem { return CollisionDetectionGridSystem{ cellSizeX: 192, cellSizeY: 192, - spatialBuckets: make(map[stdcomponents.SpatialIndex][]ecs.Entity, 32), + spatialBuckets: make(map[stdcomponents.SpatialHash][]ecs.Entity, 32), } } @@ -39,7 +37,7 @@ type CollisionDetectionGridSystem struct { GenericCollider *stdcomponents.GenericColliderComponentManager BoxColliders *stdcomponents.BoxColliderComponentManager Collisions *stdcomponents.CollisionComponentManager - SpatialIndex *stdcomponents.SpatialIndexComponentManager + SpatialIndex *stdcomponents.SpatialHashComponentManager cellSizeX int cellSizeY int @@ -47,8 +45,8 @@ type CollisionDetectionGridSystem struct { // Cache activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities currentCollisions map[CollisionPair]struct{} - spatialBuckets map[stdcomponents.SpatialIndex][]ecs.Entity - entityToCell map[ecs.Entity]stdcomponents.SpatialIndex + spatialBuckets map[stdcomponents.SpatialHash][]ecs.Entity + entityToCell map[ecs.Entity]stdcomponents.SpatialHash aabbs map[ecs.Entity]aabb } @@ -79,132 +77,132 @@ type CollisionEvent struct { } func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { - if len(s.entityToCell) < s.GenericCollider.Len() { - s.entityToCell = make(map[ecs.Entity]stdcomponents.SpatialIndex, s.GenericCollider.Len()) - } - // Reuse spatialBuckets to reduce allocations - for k := range s.spatialBuckets { - delete(s.spatialBuckets, k) - } - s.currentCollisions = make(map[CollisionPair]struct{}) - - // Build spatial buckets and entity-to-cell map - s.GenericCollider.EachEntity()(func(entity ecs.Entity) bool { - position := s.Positions.GetUnsafe(entity) - scale := s.Scales.GetUnsafe(entity) - - collider := s.GenericCollider.GetUnsafe(entity) - cellX := int(position.XY.X-(collider.Offset.X*scale.XY.X)) / s.cellSizeX - cellY := int(position.XY.Y-(collider.Offset.Y*scale.XY.Y)) / s.cellSizeY - cell := stdcomponents.SpatialIndex{X: cellX, Y: cellY} - s.entityToCell[entity] = cell - s.spatialBuckets[cell] = append(s.spatialBuckets[cell], entity) - return true - }) - - // Precompute AABBs for box colliders - s.aabbs = make(map[ecs.Entity]aabb, s.BoxColliders.Len()) - s.BoxColliders.EachEntity()(func(entity ecs.Entity) bool { - position := s.Positions.GetUnsafe(entity) - collider := s.BoxColliders.GetUnsafe(entity) - scale := s.Scales.GetUnsafe(entity) - newAABB := aabb{ - Left: position.XY.X - (collider.Offset.X * scale.XY.X), - Right: position.XY.X + (collider.WH.X-collider.Offset.X)*scale.XY.X, - Top: position.XY.Y - (collider.Offset.Y * scale.XY.Y), - Bottom: position.XY.Y + (collider.WH.Y-collider.Offset.Y)*scale.XY.Y, - } - s.aabbs[entity] = newAABB - return true - }) - - // Create collision channel - collisionChan := make(chan CollisionEvent, 4096) - doneChan := make(chan struct{}) - - // Start result collector - go func() { - for event := range collisionChan { - pair := CollisionPair{event.entityA, event.entityB}.Normalize() - s.currentCollisions[pair] = struct{}{} - - if _, exists := s.activeCollisions[pair]; !exists { - proxy := s.EntityManager.Create() - s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) - s.Positions.Create(proxy, stdcomponents.Position{ - XY: vectors.Vec2{ - X: event.position.X, - Y: event.position.Y, - }, - }) - s.activeCollisions[pair] = proxy - } else { - proxy := s.activeCollisions[pair] - s.Collisions.GetUnsafe(proxy).State = stdcomponents.CollisionStateStay - s.Positions.GetUnsafe(proxy).XY.X = event.position.X - s.Positions.GetUnsafe(proxy).XY.Y = event.position.Y - } - } - close(doneChan) - }() - - entities := s.GenericCollider.RawEntities(make([]ecs.Entity, 0, s.GenericCollider.Len())) - - // Worker pool setup - var wg sync.WaitGroup - maxNumWorkers := runtime.NumCPU() - 2 - entitiesLength := len(entities) - // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot entities - numWorkers := max(min(entitiesLength/32, maxNumWorkers), 1) - chunkSize := entitiesLength / numWorkers - - wg.Add(numWorkers) - - for i := 0; i < numWorkers; i++ { - startIndex := i * chunkSize - endIndex := startIndex + chunkSize - 1 - if i == numWorkers-1 { // have to set endIndex to entites lenght, if last worker - endIndex = entitiesLength - } - - go func(start int, end int) { - defer wg.Done() - - for _, entityA := range entities[start:end] { - collider := s.GenericCollider.GetUnsafe(entityA) - - switch collider.Shape { - case stdcomponents.BoxColliderShape: - s.boxToXCollision(entityA, collisionChan) - case stdcomponents.CircleColliderShape: - s.circleToXCollision(entityA) - default: - panic("Unknown collider shape") - } - } - }(startIndex, endIndex) - } - - // Wait for workers and close collision channel - wg.Wait() - close(collisionChan) - <-doneChan // Wait for result collector - - //s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + //if len(s.entityToCell) < s.GenericCollider.Len() { + // s.entityToCell = make(map[ecs.Entity]stdcomponents.SpatialHash, s.GenericCollider.Len()) + //} + //// Reuse spatialBuckets to reduce allocations + //for k := range s.spatialBuckets { + // delete(s.spatialBuckets, k) + //} + //s.currentCollisions = make(map[CollisionPair]struct{}) + // + //// Build spatial buckets and entity-to-cell map + //s.GenericCollider.EachEntity()(func(entity ecs.Entity) bool { + // position := s.Positions.GetUnsafe(entity) + // scale := s.Scales.GetUnsafe(entity) + // // collider := s.GenericCollider.GetUnsafe(entity) + // cellX := int(position.XY.X-(collider.Offset.X*scale.XY.X)) / s.cellSizeX + // cellY := int(position.XY.Y-(collider.Offset.Y*scale.XY.Y)) / s.cellSizeY + // cell := stdcomponents.SpatialHash{X: cellX, Y: cellY} + // s.entityToCell[entity] = cell + // s.spatialBuckets[cell] = append(s.spatialBuckets[cell], entity) + // return true + //}) // - // switch collider.Shape { - // case stdcomponents.BoxColliderShape: - // s.boxToXCollision(entity) - // case stdcomponents.CircleColliderShape: - // s.circleToXCollision(entity) - // default: - // panic("Unknown collider shape") + //// Precompute AABBs for box colliders + //s.aabbs = make(map[ecs.Entity]aabb, s.BoxColliders.Len()) + //s.BoxColliders.EachEntity()(func(entity ecs.Entity) bool { + // position := s.Positions.GetUnsafe(entity) + // collider := s.BoxColliders.GetUnsafe(entity) + // scale := s.Scales.GetUnsafe(entity) + // newAABB := aabb{ + // Left: position.XY.X - (collider.Offset.X * scale.XY.X), + // Right: position.XY.X + (collider.WH.X-collider.Offset.X)*scale.XY.X, + // Top: position.XY.Y - (collider.Offset.Y * scale.XY.Y), + // Bottom: position.XY.Y + (collider.WH.Y-collider.Offset.Y)*scale.XY.Y, // } + // s.aabbs[entity] = newAABB // return true //}) - - s.processExitStates() + // + //// Create collision channel + //collisionChan := make(chan CollisionEvent, 4096) + //doneChan := make(chan struct{}) + // + //// Start result collector + //go func() { + // for event := range collisionChan { + // pair := CollisionPair{event.entityA, event.entityB}.Normalize() + // s.currentCollisions[pair] = struct{}{} + // + // if _, exists := s.activeCollisions[pair]; !exists { + // proxy := s.EntityManager.Create() + // s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) + // s.Positions.Create(proxy, stdcomponents.Position{ + // XY: vectors.Vec2{ + // X: event.position.X, + // Y: event.position.Y, + // }, + // }) + // s.activeCollisions[pair] = proxy + // } else { + // proxy := s.activeCollisions[pair] + // s.Collisions.GetUnsafe(proxy).State = stdcomponents.CollisionStateStay + // s.Positions.GetUnsafe(proxy).XY.X = event.position.X + // s.Positions.GetUnsafe(proxy).XY.Y = event.position.Y + // } + // } + // close(doneChan) + //}() + // + //entities := s.GenericCollider.RawEntities(make([]ecs.Entity, 0, s.GenericCollider.Len())) + // + //// Worker pool setup + //var wg sync.WaitGroup + //maxNumWorkers := runtime.NumCPU() - 2 + //entitiesLength := len(entities) + //// get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot entities + //numWorkers := max(min(entitiesLength/32, maxNumWorkers), 1) + //chunkSize := entitiesLength / numWorkers + // + //wg.Add(numWorkers) + // + //for i := 0; i < numWorkers; i++ { + // startIndex := i * chunkSize + // endIndex := startIndex + chunkSize - 1 + // if i == numWorkers-1 { // have to set endIndex to entites lenght, if last worker + // endIndex = entitiesLength + // } + // + // go func(start int, end int) { + // defer wg.Done() + // + // for _, entityA := range entities[start:end] { + // collider := s.GenericCollider.GetUnsafe(entityA) + // + // switch collider.Shape { + // case stdcomponents.BoxColliderShape: + // s.boxToXCollision(entityA, collisionChan) + // case stdcomponents.CircleColliderShape: + // s.circleToXCollision(entityA) + // default: + // panic("Unknown collider shape") + // } + // } + // }(startIndex, endIndex) + //} + // + //// Wait for workers and close collision channel + //wg.Wait() + //close(collisionChan) + //<-doneChan // Wait for result collector + // + ////s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + //// collider := s.GenericCollider.GetUnsafe(entity) + //// + //// switch collider.Shape { + //// case stdcomponents.BoxColliderShape: + //// s.boxToXCollision(entity) + //// case stdcomponents.CircleColliderShape: + //// s.circleToXCollision(entity) + //// default: + //// panic("Unknown collider shape") + //// } + //// return true + ////}) + // + //s.processExitStates() } func (s *CollisionDetectionGridSystem) Destroy() {} @@ -241,64 +239,64 @@ func (s *CollisionDetectionGridSystem) processExitStates() { } func (s *CollisionDetectionGridSystem) boxToXCollision(entityA ecs.Entity, collisionChan chan<- CollisionEvent) { - position1 := s.Positions.GetUnsafe(entityA) - spatialIndex1 := s.entityToCell[entityA] - genericCollider1 := s.GenericCollider.GetUnsafe(entityA) - - var nearByIndexes = [9]stdcomponents.SpatialIndex{ - {spatialIndex1.X, spatialIndex1.Y}, - {spatialIndex1.X - 1, spatialIndex1.Y}, - {spatialIndex1.X + 1, spatialIndex1.Y}, - {spatialIndex1.X, spatialIndex1.Y - 1}, - {spatialIndex1.X, spatialIndex1.Y + 1}, - {spatialIndex1.X - 1, spatialIndex1.Y - 1}, - {spatialIndex1.X + 1, spatialIndex1.Y + 1}, - {spatialIndex1.X - 1, spatialIndex1.Y + 1}, - {spatialIndex1.X + 1, spatialIndex1.Y - 1}, - } - - for _, spatialIndex := range nearByIndexes { - bucket := s.spatialBuckets[spatialIndex] - for _, entityB := range bucket { - if entityA >= entityB { - continue // Skip duplicate checks - } - - // Broad Phase - genericCollider2 := s.GenericCollider.GetUnsafe(entityB) - if genericCollider1.Mask&(1< aabbB.Right { - continue - } - // Then Y-axis - if aabbA.Bottom < aabbB.Top || aabbA.Top > aabbB.Bottom { - continue - } - - posX := (position1.XY.X + position2.XY.X) / 2 - posY := (position1.XY.Y + position2.XY.Y) / 2 - collisionChan <- CollisionEvent{entityA, entityB, vectors.Vec2{posX, posY}, vectors.Vec2{posX, posY}, 0} - case stdcomponents.CircleColliderShape: - panic("Circle-Box collision not implemented") - default: - panic("Unknown collision shape") - } - } - } + //position1 := s.Positions.GetUnsafe(entityA) + //spatialIndex1 := s.entityToCell[entityA] + //genericCollider1 := s.GenericCollider.GetUnsafe(entityA) + // + //var nearByIndexes = [9]stdcomponents.SpatialHash{ + // {spatialIndex1.X, spatialIndex1.Y}, + // {spatialIndex1.X - 1, spatialIndex1.Y}, + // {spatialIndex1.X + 1, spatialIndex1.Y}, + // {spatialIndex1.X, spatialIndex1.Y - 1}, + // {spatialIndex1.X, spatialIndex1.Y + 1}, + // {spatialIndex1.X - 1, spatialIndex1.Y - 1}, + // {spatialIndex1.X + 1, spatialIndex1.Y + 1}, + // {spatialIndex1.X - 1, spatialIndex1.Y + 1}, + // {spatialIndex1.X + 1, spatialIndex1.Y - 1}, + //} + // + //for _, spatialIndex := range nearByIndexes { + // bucket := s.spatialBuckets[spatialIndex] + // for _, entityB := range bucket { + // if entityA >= entityB { + // continue // Skip duplicate checks + // } + // + // // Broad Phase + // genericCollider2 := s.GenericCollider.GetUnsafe(entityB) + // if genericCollider1.Mask&(1< aabbB.Right { + // continue + // } + // // Then Y-axis + // if aabbA.Bottom < aabbB.Top || aabbA.Top > aabbB.Bottom { + // continue + // } + // + // posX := (position1.XY.X + position2.XY.X) / 2 + // posY := (position1.XY.Y + position2.XY.Y) / 2 + // collisionChan <- CollisionEvent{entityA, entityB, vectors.Vec2{posX, posY}, vectors.Vec2{posX, posY}, 0} + // case stdcomponents.CircleColliderShape: + // panic("Circle-Box collision not implemented") + // default: + // panic("Unknown collision shape") + // } + // } + //} } func (s *CollisionDetectionGridSystem) circleToXCollision(entityA ecs.Entity) { diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 1ef014ec..aa11ecc3 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -15,7 +15,6 @@ Thank you for your support! package stdsystems import ( - "github.com/negrel/assert" gjk "gomp/pkg/collision" "gomp/pkg/core" "gomp/pkg/ecs" @@ -39,7 +38,7 @@ type CollisionDetectionSystem struct { CircleColliders *stdcomponents.CircleColliderComponentManager PolygonColliders *stdcomponents.PolygonColliderComponentManager Collisions *stdcomponents.CollisionComponentManager - SpatialIndex *stdcomponents.SpatialIndexComponentManager + SpatialIndex *stdcomponents.SpatialHashComponentManager AABB *stdcomponents.AABBComponentManager ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager BvhTreeComponentManager *stdcomponents.BvhTreeComponentManager @@ -104,33 +103,33 @@ func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, result []ecs.E return result } } - - aabbPtr := s.AABB.GetUnsafe(entityA) - assert.NotNil(aabbPtr) - aabb := *aabbPtr - - cells := make([]ecs.Entity, 0, 64) - - // Iterate through all trees - for index := range s.gridLookup { - grid := s.gridLookup[index] - layer := grid.Layer - - // Check if mask includes this layer - if !colliderA.Mask.HasLayer(layer) { - continue - } - - // Traverse this BVH tree for potential collisions - cells = grid.Query(aabb, cells) - } - - for _, cellEntityId := range cells { - tree := s.BvhTreeComponentManager.GetUnsafe(cellEntityId) - assert.NotNil(tree) - - result = tree.Query(aabb, result) - } + // + //aabbPtr := s.AABB.GetUnsafe(entityA) + //assert.NotNil(aabbPtr) + //aabb := *aabbPtr + // + //cells := make([]ecs.Entity, 0, 64) + // + //// Iterate through all trees + //for index := range s.gridLookup { + // grid := s.gridLookup[index] + // layer := grid.Layer + // + // // Check if mask includes this layer + // if !colliderA.Mask.HasLayer(layer) { + // continue + // } + // + // // Traverse this BVH tree for potential collisions + // cells = grid.Query(aabb, cells) + //} + // + //for _, cellEntityId := range cells { + // tree := s.BvhTreeComponentManager.GetUnsafe(cellEntityId) + // assert.NotNil(tree) + // + // result = tree.Query(aabb, result) + //} return result } diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index e8d5b496..1e70747f 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -9,6 +9,8 @@ Donations during this file development: <- SeniorOverflow Donated 500 RUB <- SeniorOverflow Donated 1 000 RUB +<- Монтажёр сука Donated 100 RUB +<- Монтажёр сука Donated 100 RUB Thank you for your support! */ @@ -16,16 +18,15 @@ Thank you for your support! package stdsystems import ( - "github.com/negrel/assert" "gomp/pkg/core" "gomp/pkg/ecs" "gomp/pkg/worker" "gomp/stdcomponents" - "gomp/vectors" "image/color" - "math" "math/rand" "time" + + "github.com/negrel/assert" ) const ( @@ -37,275 +38,296 @@ func NewCollisionSetupSystem() CollisionSetupSystem { } type CollisionSetupSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - Tints *stdcomponents.TintComponentManager - GenericCollider *stdcomponents.GenericColliderComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - CircleColliders *stdcomponents.CircleColliderComponentManager - PolygonColliders *stdcomponents.PolygonColliderComponentManager - Collisions *stdcomponents.CollisionComponentManager - SpatialIndex *stdcomponents.SpatialIndexComponentManager - AABB *stdcomponents.AABBComponentManager - ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager - BvhTreeComponentManager *stdcomponents.BvhTreeComponentManager - CollisionGridComponentManager *stdcomponents.CollisionGridComponentManager - CollisionChunkComponentManager *stdcomponents.CollisionChunkComponentManager - CollisionCellComponentManager *stdcomponents.CollisionCellComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Tints *stdcomponents.TintComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + PolygonColliders *stdcomponents.PolygonColliderComponentManager + Collisions *stdcomponents.CollisionComponentManager + SpatialHash *stdcomponents.SpatialHashComponentManager + AABB *stdcomponents.AABBComponentManager + ColliderSleepStateComponentManager *stdcomponents.ColliderSleepStateComponentManager + BvhTreeComponentManager *stdcomponents.BvhTreeComponentManager + CollisionGridComponentManager *stdcomponents.CollisionGridComponentManager + CollisionChunkComponentManager *stdcomponents.CollisionChunkComponentManager + CollisionCellComponentManager *stdcomponents.CollisionCellComponentManager + CollisionGridMemberComponentManager *stdcomponents.CollisionGridMemberComponentManager gridLookup map[stdcomponents.CollisionLayer]ecs.Entity Engine *core.Engine + + clearCellAccumulator []ecs.PagedArray[ecs.Entity] + + memberListPool stdcomponents.MemberListPool } +// для грида для каждого потока мапа ключ хэш значение сущность +// по каждой клетке в многопотоке и проверить по ключу всех жителей? + func (s *CollisionSetupSystem) Init() { - s.gridLookup = make(map[stdcomponents.CollisionLayer]ecs.Entity) + s.memberListPool = stdcomponents.NewMemberListPool(s.Engine.Pool()) + s.clearCellAccumulator = make([]ecs.PagedArray[ecs.Entity], s.Engine.Pool().NumWorkers()) + for i := range s.Engine.Pool().NumWorkers() { + s.clearCellAccumulator[i] = ecs.NewPagedArray[ecs.Entity]() + } + s.gridLookup = make(map[stdcomponents.CollisionLayer]ecs.Entity, 64) } + func (s *CollisionSetupSystem) Run(dt time.Duration) { - // Reset grids - s.CollisionGridComponentManager.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { + s.setup() +} + +func (s *CollisionSetupSystem) Destroy() { + s.gridLookup = nil +} +func (s *CollisionSetupSystem) setup() { + // Prepare grids + s.CollisionGridComponentManager.EachEntity()(func(entity ecs.Entity) bool { grid := s.CollisionGridComponentManager.GetUnsafe(entity) assert.NotNil(grid) - grid.Entities.Reset() - grid.MinBounds = vectors.Vec2{ - X: math.MaxFloat32, - Y: math.MaxFloat32, + + s.gridLookup[grid.Layer] = entity + + if grid.CreateCellsAccumulator == nil { + grid.CreateCellsAccumulator = make([]ecs.GenMap[stdcomponents.SpatialCellIndex, struct{}], s.Engine.Pool().NumWorkers()) + for i := range grid.CreateCellsAccumulator { + grid.CreateCellsAccumulator[i] = ecs.NewGenMap[stdcomponents.SpatialCellIndex, struct{}](1024) + } } - }) - // Accumulate used CollisionLayers - var collisionLayerAccumulators = make([]stdcomponents.CollisionLayer, s.Engine.Pool().NumWorkers()) - s.GenericCollider.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { - collider := s.GenericCollider.GetUnsafe(entity) - assert.NotNil(collider) - collisionLayerAccumulators[workerId] |= 1 << collider.Layer + return true }) - collisionLayerAccumulator := stdcomponents.CollisionLayer(0) - for _, mask := range collisionLayerAccumulators { - collisionLayerAccumulator |= mask - } - // For each bit in collisionLayerAccumulators, create a grid if it doesn't exist - for i := uint(0); i < 32; i++ { - if collisionLayerAccumulator&(1<= 0; i-- { + member := memberList.Members[i] + spatialHash := s.SpatialHash.GetUnsafe(member) + assert.NotNil(spatialHash) + + // Check if entity is in cell + if cell.Index.X >= spatialHash.Min.X && cell.Index.X <= spatialHash.Max.X { + if cell.Index.Y >= spatialHash.Min.Y && cell.Index.Y <= spatialHash.Max.Y { + continue + } } - grid.CellAccumulator[i] = make(map[stdcomponents.SpatialIndex]struct{}) + + // Entity should be removed from cell + memberList.Members = append(memberList.Members[:i], memberList.Members[i+1:]...) + memberList.Lookup.Delete(member) } + memberList.Lookup.Clear() }) // Distribute entities in grid cells - s.GenericCollider.ProcessEntities(func(entity ecs.Entity, id worker.WorkerId) { - collider := s.GenericCollider.GetUnsafe(entity) - assert.NotNil(collider) + s.SpatialHash.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { + spatialHash := s.SpatialHash.GetUnsafe(entity) + assert.NotNil(spatialHash) - gridEntity, ok := s.gridLookup[collider.Layer] - assert.True(ok) + gridMember := s.CollisionGridMemberComponentManager.GetUnsafe(entity) + assert.NotNil(gridMember) - grid := s.CollisionGridComponentManager.GetUnsafe(gridEntity) + grid := s.CollisionGridComponentManager.GetUnsafe(gridMember.Grid) assert.NotNil(grid) - spatialIndex := s.SpatialIndex.GetUnsafe(entity) - assert.NotNil(spatialIndex) + for i := spatialHash.Min.X; i <= spatialHash.Max.X; i++ { + for j := spatialHash.Min.Y; j <= spatialHash.Max.Y; j++ { + cellEntity, exists := grid.CellMap.Get(stdcomponents.SpatialCellIndex{X: i, Y: j}) + assert.True(exists) - cellEntityLookup, exists := grid.CellLookup[*spatialIndex] - assert.True(exists) - cellEntity := grid.Cells.GetValue(cellEntityLookup) + cell := s.CollisionCellComponentManager.GetUnsafe(cellEntity) + assert.NotNil(cell) + members := cell.Members + assert.NotNil(members) - cell := s.CollisionCellComponentManager.GetUnsafe(cellEntity) - assert.NotNil(cell) + if members.Has(entity) { + continue + } - //_, exists = cell.MemberLookup.Get(entity) - //if exists { - // return - //} - cell.InputAccumulator[id].Append(entity) + members.InputAcc[workerId] = append(members.InputAcc[workerId], entity) + } + } }) //Build grid cells - //s.CollisionCellComponentManager.ProcessComponents(func(cell *stdcomponents.CollisionCell, id worker.WorkerId) { - // for i := range cell.InputAccumulator { - // for e := range cell.InputAccumulator[i].EachDataValue() { - // cell.AddMember(e) - // } - // cell.InputAccumulator[i].Reset() - // } - //}) - - //Build bvh trees - s.CollisionCellComponentManager.ProcessEntities(func(entity ecs.Entity, id worker.WorkerId) { - cell := s.CollisionCellComponentManager.GetUnsafe(entity) - assert.NotNil(cell) + s.CollisionCellComponentManager.ProcessComponents(func(cell *stdcomponents.CollisionCell, workerId worker.WorkerId) { + members := cell.Members + for i := range members.InputAcc { + acc := members.InputAcc[i] + for j := range acc { + members.Add(acc[j]) + } + members.InputAcc[i] = acc[:0] + } + }) - bvhTree := s.BvhTreeComponentManager.GetUnsafe(entity) - assert.NotNil(bvhTree) + //Remove empty cells + s.CollisionCellComponentManager.ProcessEntities(func(cellEntity ecs.Entity, workerId worker.WorkerId) { + cell := s.CollisionCellComponentManager.GetUnsafe(cellEntity) + assert.NotNil(cell) - for i := range cell.InputAccumulator { - for e := range cell.InputAccumulator[i].EachDataValue() { - aabb := s.AABB.GetUnsafe(e) - assert.NotNil(aabb) - bvhTree.AddComponent(e, *aabb) - } - cell.InputAccumulator[i].Reset() + if len(cell.Members.Members) != 0 { + return } + s.memberListPool.Put(cell.Members) + cell.Members = nil + s.clearCellAccumulator[workerId].Append(cellEntity) }) - s.CollisionCellComponentManager.ProcessEntities(func(entity ecs.Entity, id worker.WorkerId) { - bvhTree := s.BvhTreeComponentManager.GetUnsafe(entity) - assert.NotNil(bvhTree) + for i := range s.clearCellAccumulator { + v := &s.clearCellAccumulator[i] + v.EachDataValue()(func(cellEntity ecs.Entity) bool { + cell := s.CollisionCellComponentManager.GetUnsafe(cellEntity) + assert.NotNil(cell) - bvhTree.Build() + grid := s.CollisionGridComponentManager.GetUnsafe(cell.Grid) + assert.NotNil(grid) + + grid.CellMap.Delete(cell.Index) + return true + }) + v.EachDataValue()(func(cellEntity ecs.Entity) bool { + s.EntityManager.Delete(cellEntity) + return true + }) + v.Reset() + } + s.CollisionGridComponentManager.ProcessComponents(func(grid *stdcomponents.CollisionGrid, workerId worker.WorkerId) { + grid.CellMap.Clear() }) } -func (s *CollisionSetupSystem) Destroy() { - s.gridLookup = nil +func (s *CollisionSetupSystem) detection() { } diff --git a/stdsystems/debug.go b/stdsystems/debug.go index 2760de63..22ed7b6d 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -7,14 +7,15 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdsystems import ( - "github.com/felixge/fgprof" - rl "github.com/gen2brain/raylib-go/raylib" "log" "net/http" _ "net/http/pprof" "os" "runtime/pprof" "runtime/trace" + + "github.com/felixge/fgprof" + rl "github.com/gen2brain/raylib-go/raylib" ) const gpprof = false @@ -91,7 +92,7 @@ func (s *DebugSystem) Run() { if s.traceEnabled { s.traceCounter++ - if s.traceCounter == 10 { + if s.traceCounter == 50 { trace.Stop() s.traceEnabled = false s.traceCounter = 0 diff --git a/taskfile.yml b/taskfile.yml index 4ca098f4..33a93063 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -7,6 +7,12 @@ tasks: cmds: - go run ./examples/new-api + dev-assert: + env: + CGO_ENABLED: 1 + cmds: + - go run ./examples/new-api -tags assert + server: cmds: - go run ./examples/server . @@ -34,7 +40,7 @@ tasks: gpprof-cpu: cmds: - - go tool pprof --http=:6061 http://localhost:6060/debug/fgprof?seconds=20 + - go tool pprof --http=:6061 http://localhost:6060/debug/fgprof?seconds=20 pprof-cpu: cmds: From 0a4c967f6decac537621768dd860e7de01b3899e Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 27 Apr 2025 15:10:20 +0300 Subject: [PATCH 178/196] refactor(spatial-audio): adds volume calculation and fix volume control Sometime only for one fram audio spikes in volume. Thats may happen because of system execution order where differrent systems changes underlying raylib sound object. This commits adds new system to control sound settings (volume, pan and pitch). style(formating): go fmt --- examples/new-api/assets/assets.go | 3 + examples/new-api/components/audio.go | 13 +++ examples/new-api/components/ids.go | 1 + examples/new-api/game.go | 3 + examples/new-api/instances/component-list.go | 2 + examples/new-api/instances/system-list.go | 2 + examples/new-api/systems/audio.go | 47 +++++++++-- examples/new-api/systems/spaceship-intents.go | 10 +++ examples/new-api/systems/spatial-audio.go | 84 +++++++++++++------ 9 files changed, 132 insertions(+), 33 deletions(-) diff --git a/examples/new-api/assets/assets.go b/examples/new-api/assets/assets.go index 63ef3b75..7a967025 100644 --- a/examples/new-api/assets/assets.go +++ b/examples/new-api/assets/assets.go @@ -68,6 +68,9 @@ var Audio = gomp.CreateAssetLibrary( assert.True(rl.IsSoundValid(sound), "Error loading sound") + // Mute sound by default. Later it controlled by Aduio systems + rl.SetSoundVolume(sound, 0) + return sound }, func(path string, asset *rl.Sound) { diff --git a/examples/new-api/components/audio.go b/examples/new-api/components/audio.go index 079a7a08..d5bab4c0 100644 --- a/examples/new-api/components/audio.go +++ b/examples/new-api/components/audio.go @@ -36,3 +36,16 @@ type SoundEffectsComponentManager = ecs.ComponentManager[SoundEffect] func NewSoundEffectsComponentManager() SoundEffectsComponentManager { return ecs.NewComponentManager[SoundEffect](SoundEffectManagerComponentId) } + +type SpatialAudio struct { + // follows raylib rules. Base is 1.0 + Volume float32 + // follows raylib rules. Base is 0.5, 1.0 is left, 0.0 is right + Pan float32 +} + +type SpatialAudioComponentManager = ecs.ComponentManager[SpatialAudio] + +func NewSpatialAudioComponentManager() SpatialAudioComponentManager { + return ecs.NewComponentManager[SpatialAudio](SpatialAudioManagerComponentId) +} diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go index 74ccd2ba..f6063132 100644 --- a/examples/new-api/components/ids.go +++ b/examples/new-api/components/ids.go @@ -30,6 +30,7 @@ const ( SpaceshipIntentComponentId AsteroidSceneManagerComponentId SoundEffectManagerComponentId + SpatialAudioManagerComponentId TextureRectComponentId TextureCircleComponentId ) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 9596e2a5..878b7038 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -79,6 +79,7 @@ func (g *Game) Init(engine *core.Engine) { systems.AssetLib.Init() systems.Audio.Init() systems.SpatialAudio.Init() + systems.AudioSettings.Init() systems.RenderCameras.Init() systems.Culling.Init() systems.TexturePositionSmooth.Init() @@ -128,6 +129,7 @@ func (g *Game) Render(dt time.Duration) { systems.YSort.Run() systems.Audio.Run(dt) systems.SpatialAudio.Run(dt) + systems.AudioSettings.Run(dt) scene.Render(dt) @@ -160,6 +162,7 @@ func (g *Game) Destroy() { systems.AssetLib.Destroy() systems.Audio.Destroy() systems.SpatialAudio.Destroy() + systems.AudioSettings.Destroy() systems.RenderCameras.Destroy() systems.Culling.Destroy() systems.TexturePositionSmooth.Destroy() diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index e2a39fdd..daff7de5 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -63,6 +63,7 @@ type ComponentList struct { SpaceshipIntent components.SpaceshipIntentComponentManager AsteroidSceneManager components.AsteroidSceneManagerComponentManager SoundEffects components.SoundEffectsComponentManager + SpatialAudio components.SpatialAudioComponentManager TextureRect components.TextureRectComponentManager PrimitiveCircle components.PrimitiveCircleComponentManager RenderVisible stdcomponents.RenderVisibleComponentManager @@ -114,6 +115,7 @@ func NewComponentList() ComponentList { SpaceshipIntent: components.NewSpaceshipIntentComponentManager(), AsteroidSceneManager: components.NewAsteroidSceneManagerComponentManager(), SoundEffects: components.NewSoundEffectsComponentManager(), + SpatialAudio: components.NewSpatialAudioComponentManager(), TextureRect: components.NewTextureRectComponentManager(), PrimitiveCircle: components.NewTextureCircleComponentManager(), } diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index 07a827cc..39279b12 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -47,6 +47,7 @@ func NewSystemList() SystemList { Player: systems.NewPlayerSystem(), RenderBogdan: systems.NewRenderBogdanSystem(), Audio: systems.NewAudioSystem(), + AudioSettings: systems.NewAudioSettingsSystem(), SpatialAudio: systems.NewSpatialAudioSystem(), DampingSystem: systems.NewDampingSystem(), AssteroddSystem: systems.NewAssteroddSystem(), @@ -90,6 +91,7 @@ type SystemList struct { RenderBogdan systems.RenderBogdanSystem Player systems.PlayerSystem Audio systems.AudioSystem + AudioSettings systems.AudioSettingsSystem SpatialAudio systems.SpatialAudioSystem DampingSystem systems.DampingSystem AssteroddSystem systems.AssteroddSystem diff --git a/examples/new-api/systems/audio.go b/examples/new-api/systems/audio.go index 70d67641..8c79bf95 100644 --- a/examples/new-api/systems/audio.go +++ b/examples/new-api/systems/audio.go @@ -52,20 +52,12 @@ func (s *AudioSystem) Run(dt time.Duration) { } else { *clip = rl.LoadSoundAlias(*clip) - rl.SetSoundVolume(*clip, soundEffect.Volume) - rl.SetSoundPitch(*clip, soundEffect.Pitch) - rl.SetSoundPan(*clip, soundEffect.Pan) - rl.PlaySound(*clip) soundEffect.IsPlaying = true return true } } - rl.SetSoundVolume(*clip, soundEffect.Volume) - rl.SetSoundPitch(*clip, soundEffect.Pitch) - rl.SetSoundPan(*clip, soundEffect.Pan) - // check if sound is over if !rl.IsSoundPlaying(*clip) && soundEffect.IsPlaying { if soundEffect.IsLooping { @@ -83,3 +75,42 @@ func (s *AudioSystem) Run(dt time.Duration) { func (s *AudioSystem) Destroy() { rl.CloseAudioDevice() } + +func NewAudioSettingsSystem() AudioSettingsSystem { + return AudioSettingsSystem{} +} + +type AudioSettingsSystem struct { + EntityManager *ecs.EntityManager + SoundEffects *components.SoundEffectsComponentManager + SpatialAudio *components.SpatialAudioComponentManager +} + +func (s *AudioSettingsSystem) Init() {} + +func (s *AudioSettingsSystem) Run(dt time.Duration) { + s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { + soundEffect := s.SoundEffects.GetUnsafe(entity) + clip := soundEffect.Clip + + // check if clip is loaded + if clip == nil { + return true + } + + spatialSettings := s.SpatialAudio.GetUnsafe(entity) + + if spatialSettings != nil { + rl.SetSoundVolume(*clip, spatialSettings.Volume*soundEffect.Volume) + rl.SetSoundPan(*clip, spatialSettings.Pan) + } else { + rl.SetSoundVolume(*clip, soundEffect.Volume) + rl.SetSoundPan(*clip, soundEffect.Pan) + } + + rl.SetSoundPitch(*clip, soundEffect.Pitch) + return true + }) +} +func (s *AudioSettingsSystem) Destroy() { +} diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index fe8d22df..f13ac5ec 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -144,6 +144,16 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { Volume: 1.0, Pan: 0.5, }) + + s.Positions.Create( + fireSoundEntity, + stdcomponents.Position{ + XY: vectors.Vec2{ + X: pos.XY.X, + Y: pos.XY.Y, + }, + }, + ) } } else { weapon.CooldownLeft -= dt diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go index be65a4ea..5fb9cbe3 100644 --- a/examples/new-api/systems/spatial-audio.go +++ b/examples/new-api/systems/spatial-audio.go @@ -16,13 +16,16 @@ package systems import ( "gomp/examples/new-api/components" + "gomp/examples/new-api/config" "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" "math" "time" +) - rl "github.com/gen2brain/raylib-go/raylib" +const ( + MinDistance = 10 ) func NewSpatialAudioSystem() SpatialAudioSystem { @@ -33,27 +36,34 @@ type SpatialAudioSystem struct { EntityManager *ecs.EntityManager SoundEffects *components.SoundEffectsComponentManager Positions *stdcomponents.PositionComponentManager - Player *components.PlayerTagComponentManager + SpatialAudio *components.SpatialAudioComponentManager + Cameras *stdcomponents.CameraComponentManager } func (s *SpatialAudioSystem) Init() { } func (s *SpatialAudioSystem) Run(dt time.Duration) { - var player ecs.Entity = 0 + var mainCamera ecs.Entity + + // TODO: Add listener component? Then we need position component on it... + s.Cameras.EachEntity()(func(entity ecs.Entity) bool { + camera := s.Cameras.GetUnsafe(entity) + if camera.Layer == config.MainCameraLayer { + mainCamera = entity + return false + } - s.Player.EachEntity()(func(entity ecs.Entity) bool { - player = entity - return false + return true }) - if player == 0 { + if mainCamera == 0 { return } - playerPos := s.Positions.GetUnsafe(player) - - if playerPos == nil { - return + mainCameraComponent := s.Cameras.GetUnsafe(mainCamera) + var mainCameraPosition vectors.Vec2 = vectors.Vec2{ + X: mainCameraComponent.Camera2D.Target.X, + Y: mainCameraComponent.Camera2D.Target.Y, } s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { @@ -65,18 +75,31 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { return true } - if !soundEffect.IsPlaying { - return true - } - position := s.Positions.GetUnsafe(entity) if position == nil { return true } - pan := s.calculatePan(playerPos.XY, position.XY) - rl.SetSoundPan(*clip, pan) + spatialAudio := s.SpatialAudio.GetUnsafe(entity) + + if spatialAudio == nil { + spatialAudio = s.SpatialAudio.Create(entity, components.SpatialAudio{ + Volume: 0, + Pan: 0.5, + }) + } + + spatialAudio.Volume = s.calculateVolume( + mainCameraPosition, + position.XY, + mainCameraComponent.Camera2D.Offset.X*2, + ) + spatialAudio.Pan = s.calculatePan( + mainCameraPosition, + position.XY, + mainCameraComponent.Camera2D.Offset.X*2, + ) return true }) @@ -84,18 +107,29 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { func (s *SpatialAudioSystem) Destroy() { } -func (s *SpatialAudioSystem) calculatePan(listener vectors.Vec2, source vectors.Vec2) float32 { - dx := float64(source.X - listener.X) - dy := float64(source.Y - listener.Y) - distanceSq := dx*dx + dy*dy +func (s *SpatialAudioSystem) calculatePan(listener vectors.Vec2, source vectors.Vec2, maxDistance float32) float32 { + distance := listener.Distance(source) - // Если источник и слушатель в одной точке - if distanceSq < 1e-9 { // Используем квадрат расстояния для оптимизации + if distance < MinDistance { return 0.5 } - distance := math.Sqrt(distanceSq) - pan := 0.5 - (dx / (2 * distance)) + distanceX := float64(listener.X - source.X) + + pan := (1 + (distanceX / float64(maxDistance))) / 2 return float32(math.Max(0, math.Min(1, pan))) } + +func (s *SpatialAudioSystem) calculateVolume(listener vectors.Vec2, source vectors.Vec2, maxDistance float32) float32 { + distance := float64(listener.Distance(source)) + + if distance < MinDistance { + return 1 + } + + spatialVolume := 1 - (distance / float64(maxDistance)) // TODO: add ability to configure volume hearing distance + volume := math.Max(0, math.Min(1, spatialVolume)) + + return float32(math.Sin((volume * math.Pi) / 2)) // TODO: add ability to configure volume falloff. Current is easeOutSine +} From 4de6c17cc08675b7fc9b2e428b5d342ef7b0fb08 Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 27 Apr 2025 15:50:47 +0300 Subject: [PATCH 179/196] chore(dev): adds asserts on GetUnsafe components --- examples/new-api/entities/spaceship.go | 2 +- examples/new-api/systems/audio.go | 9 +++++++-- examples/new-api/systems/spatial-audio.go | 3 +++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go index d7c93fae..354ad5b4 100644 --- a/examples/new-api/entities/spaceship.go +++ b/examples/new-api/entities/spaceship.go @@ -123,7 +123,7 @@ func CreateSpaceShip( IsPlaying: false, IsLooping: true, Pitch: 1.0, - Volume: 1.0, + Volume: 0.0, Pan: 0.5, }) diff --git a/examples/new-api/systems/audio.go b/examples/new-api/systems/audio.go index 8c79bf95..c7cf3a1d 100644 --- a/examples/new-api/systems/audio.go +++ b/examples/new-api/systems/audio.go @@ -15,6 +15,7 @@ Thank you for your support! package systems import ( + "github.com/negrel/assert" "gomp/examples/new-api/components" "gomp/pkg/ecs" "time" @@ -38,10 +39,12 @@ func (s *AudioSystem) Init() { func (s *AudioSystem) Run(dt time.Duration) { s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { soundEffect := s.SoundEffects.GetUnsafe(entity) + assert.NotNil(soundEffect) + clip := soundEffect.Clip - // check if clip is valid - if clip == nil || clip.FrameCount == 0 { + // check if clip is loaded + if clip == nil || !rl.IsSoundValid(*clip) { return true } @@ -91,6 +94,8 @@ func (s *AudioSettingsSystem) Init() {} func (s *AudioSettingsSystem) Run(dt time.Duration) { s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { soundEffect := s.SoundEffects.GetUnsafe(entity) + assert.NotNil(soundEffect) + clip := soundEffect.Clip // check if clip is loaded diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go index 5fb9cbe3..be048456 100644 --- a/examples/new-api/systems/spatial-audio.go +++ b/examples/new-api/systems/spatial-audio.go @@ -22,6 +22,8 @@ import ( "gomp/vectors" "math" "time" + + "github.com/negrel/assert" ) const ( @@ -68,6 +70,7 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { soundEffect := s.SoundEffects.GetUnsafe(entity) + assert.NotNil(soundEffect) clip := soundEffect.Clip From 5744efe5fec57b584dcfef4e104a5ce13d7daac8 Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 27 Apr 2025 15:51:12 +0300 Subject: [PATCH 180/196] docs(sound-effect): adds more description on component fields chore(dev): adds more asserts on SpatialAudioSystem --- examples/new-api/components/audio.go | 5 ++++- examples/new-api/systems/spatial-audio.go | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/new-api/components/audio.go b/examples/new-api/components/audio.go index d5bab4c0..a233a3ca 100644 --- a/examples/new-api/components/audio.go +++ b/examples/new-api/components/audio.go @@ -20,8 +20,11 @@ import ( ) type SoundEffect struct { - Clip *rl.Sound + // audio clip from assets folder + Clip *rl.Sound + // internal flag, should be false by default IsPlaying bool + // should sound be looped. Default is false. If not looped, entity will be destroyed IsLooping bool // base is 1.0 Volume float32 diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go index be048456..9a4d4ee6 100644 --- a/examples/new-api/systems/spatial-audio.go +++ b/examples/new-api/systems/spatial-audio.go @@ -50,6 +50,7 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { // TODO: Add listener component? Then we need position component on it... s.Cameras.EachEntity()(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) + assert.NotNil(camera) if camera.Layer == config.MainCameraLayer { mainCamera = entity return false @@ -63,6 +64,8 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { } mainCameraComponent := s.Cameras.GetUnsafe(mainCamera) + assert.NotNil(mainCameraComponent) + var mainCameraPosition vectors.Vec2 = vectors.Vec2{ X: mainCameraComponent.Camera2D.Target.X, Y: mainCameraComponent.Camera2D.Target.Y, From 46aa58f5e9ac2d85b7a3356825428489793a54c6 Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 27 Apr 2025 16:27:44 +0300 Subject: [PATCH 181/196] refactor(spatial-audio): rewrite SpatialAudioSystem to parallel processing docs(audio): clarify comment for Clip --- examples/new-api/components/audio.go | 2 +- examples/new-api/systems/spatial-audio.go | 76 ++++++++++++++++------- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/examples/new-api/components/audio.go b/examples/new-api/components/audio.go index a233a3ca..93032d0a 100644 --- a/examples/new-api/components/audio.go +++ b/examples/new-api/components/audio.go @@ -20,7 +20,7 @@ import ( ) type SoundEffect struct { - // audio clip from assets folder + // audio clip from assets Clip *rl.Sound // internal flag, should be false by default IsPlaying bool diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go index 9a4d4ee6..469a7651 100644 --- a/examples/new-api/systems/spatial-audio.go +++ b/examples/new-api/systems/spatial-audio.go @@ -17,7 +17,9 @@ package systems import ( "gomp/examples/new-api/components" "gomp/examples/new-api/config" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" "math" @@ -35,15 +37,23 @@ func NewSpatialAudioSystem() SpatialAudioSystem { } type SpatialAudioSystem struct { - EntityManager *ecs.EntityManager - SoundEffects *components.SoundEffectsComponentManager - Positions *stdcomponents.PositionComponentManager - SpatialAudio *components.SpatialAudioComponentManager - Cameras *stdcomponents.CameraComponentManager + EntityManager *ecs.EntityManager + SoundEffects *components.SoundEffectsComponentManager + Positions *stdcomponents.PositionComponentManager + SpatialAudio *components.SpatialAudioComponentManager + Cameras *stdcomponents.CameraComponentManager + numWorkers int + accSpatialAudioCreate [][]ecs.Entity + accSpatialAudioDelete [][]ecs.Entity + Engine *core.Engine } func (s *SpatialAudioSystem) Init() { + s.numWorkers = s.Engine.Pool().NumWorkers() + s.accSpatialAudioCreate = make([][]ecs.Entity, s.numWorkers) + s.accSpatialAudioDelete = make([][]ecs.Entity, s.numWorkers) } + func (s *SpatialAudioSystem) Run(dt time.Duration) { var mainCamera ecs.Entity @@ -71,30 +81,41 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { Y: mainCameraComponent.Camera2D.Target.Y, } - s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { - soundEffect := s.SoundEffects.GetUnsafe(entity) - assert.NotNil(soundEffect) - - clip := soundEffect.Clip - - if clip == nil { - return true + s.SoundEffects.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { + position := s.Positions.Has(entity) + + if s.SpatialAudio.Has(entity) { + if !position { + s.accSpatialAudioDelete[workerId] = append(s.accSpatialAudioDelete[workerId], entity) + } + } else { + if position { + s.accSpatialAudioCreate[workerId] = append(s.accSpatialAudioCreate[workerId], entity) + } } + }) - position := s.Positions.GetUnsafe(entity) + for a := range s.accSpatialAudioCreate { + for _, entity := range s.accSpatialAudioCreate[a] { + s.SpatialAudio.Create(entity, components.SpatialAudio{ + Volume: 0, + Pan: 0.5, + }) + } + } - if position == nil { - return true + for a := range s.accSpatialAudioDelete { + for _, entity := range s.accSpatialAudioDelete[a] { + s.SpatialAudio.Delete(entity) } + } + s.SpatialAudio.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { spatialAudio := s.SpatialAudio.GetUnsafe(entity) + assert.NotNil(spatialAudio) - if spatialAudio == nil { - spatialAudio = s.SpatialAudio.Create(entity, components.SpatialAudio{ - Volume: 0, - Pan: 0.5, - }) - } + position := s.Positions.GetUnsafe(entity) + assert.NotNil(position) spatialAudio.Volume = s.calculateVolume( mainCameraPosition, @@ -106,9 +127,16 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { position.XY, mainCameraComponent.Camera2D.Offset.X*2, ) - - return true }) + + for i := range s.accSpatialAudioCreate { + s.accSpatialAudioCreate[i] = s.accSpatialAudioCreate[i][:0] + } + + for i := range s.accSpatialAudioDelete { + s.accSpatialAudioDelete[i] = s.accSpatialAudioDelete[i][:0] + } + } func (s *SpatialAudioSystem) Destroy() { } From 697ed2a66bcc81c9de5fb713e20d020171fc4047 Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 27 Apr 2025 16:54:27 +0300 Subject: [PATCH 182/196] feat(sound-effects): adds sound for satellite to demo SpatialAudioSystem --- .../new-api/assets/satellite_main_sound.wav | Bin 0 -> 1411280 bytes examples/new-api/entities/satellite.go | 14 +++++++++++++- examples/new-api/systems/asterodd.go | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 examples/new-api/assets/satellite_main_sound.wav diff --git a/examples/new-api/assets/satellite_main_sound.wav b/examples/new-api/assets/satellite_main_sound.wav new file mode 100644 index 0000000000000000000000000000000000000000..a9d37070b6643b85d92dfc8328fb3c9ee5b23d06 GIT binary patch literal 1411280 zcmXV1WmFqX(+vp;Ar1t0cPZ}f?ykiOg}S?Ykpe{v1&X(&lp>|LYp~!D+&zQ<2_)b1 zp7YK9IeT`0%(**vW_F#e%*{LBume1d+|8`r>}~h}0D$X-P@%A!H*Q`F6^ghn6c!pC z6csA;{}vW~LkRQV8U_FmVEP>Zz?1<2i~tNEBfrL@^X9V@G!uLxd|&ubWKSetG+Q)5G*+}rq*cUG*jCt3P)D$YuYm72 z_fPIl!nZ66iKgv43u^YMfe)aAeW^QjzYT|f;IP`1yQ=b-&x9ewDNt;BwO-oq|vN@>v zut~ES++xt8*%saQva_ypvX{G0V(8kC-FV@+%53gz#fskQuN|jd8oB1^7Tpd&WLkjQ zU|d+dxo&Zv2}%pIN?J+Xm#97xCUmYebrO>ib%O%`u(m_Ml$9Zi|7p zI6eHatGee#rr!kT&HduaaJSTzU2Tvq-27fzmfDn=YSmanRTzo?Kmt>acu(Z86UUo~O zO8%+TBSlsj`E$9l?aI3HE~*O(LuzSC_8O7Oothr17TO`|gF1mWt&O+&vsa6R!3L2EawDIZMP1eDUUV(2j1I3tbRQq$pQDm z0q3_OOhR~~Ji}%)8SE5T5D?`n?F07Ibr*8kbZoOTu%TKQnROeT>Kp6a)4ZtKan4zOTLvmY z6-^PK@?km8*gv2VXcSZh`UCKs;pPd1dYjlmYTg{)30_uRzc72SI6c8L+l?=u)E#IV z)$6qz9_sWSU})d&>urJbDmUkMt2Fg@^*27~0yGtOH8r_)BU*0vOt!-NggRjT<6V)1 zlkM!1R~-vbpoi9LpN(57&{~)7#^F!o+tZLo&y)0_8H5aQ2aA z!4S+4#JB+xX5xfAWnN^u2e)8`AoXFws8(1TY6M0?MZ=0wG0ZDSRcHw!6mkL+WE_G< zGU!4qX*!@r$}oMLj5tjtZXdyR9v`T$ckW0o;n!CP-)Gt(L^Hl$$vi&pOMwBD@cYr8uumfZQRww#kl6glKA+f`A?IdA10=!JpPPJBcxpUl9qn^YjGwa7nYMz zP@Y#*^0kOs+Wq6hAGzPQe`_mE>tFs0Y`N7K)A_Kir7x-baHz6hV0;3vI&(CkumGKf zu7a1UH#gRF_kM1FAlVWNj+@ADPQg?q02e(2bO7vuBthz#PnmBc#F1na8^(a8h1HEs zmtCLz5htA^misH$bzX5E75>k>!vYjOKOql+QQ=BKb5U;LatJhB`doI9;2Y}3rxECdXhjC{3Qv;$NfRh1N~<)7zt=ntayM|jT2aJ1v$khkj6vrdCvqp+9fOaMY0x^_;TR?M6>X15S z52!Aj59Wq=1_vXbAN zjv}bN#HUBBJGKX`>%%)^i$WXG1l{Gian(8PVV0@aeX}E@9Yq7m&G&i=YJ=L1t3sR5 zzY}V?e~_!93RB9l-=3FpeyuNgoZ(XFl4_Ng`Z+JhHSyBdcb^I~_2S*WT#37rAri-$ z;SwjB0f;Zoko=VQr7Z!Sg-=@hYM&yRtCZfAcPX>A5SYXMeL3$}DXKWO{QA%0Dp>j9 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%0A>@iVfPFO2&MncHjbOcZpb93gru3mcE@)%d}+% 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 z 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

    w5~UBbo40!cV@ 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
    $eFr?vk~CPl_yy>_(BxX&n{O`=d@%pTUu7uc z9n?*FV3Sk@E z1zYbGi>#WW{kExN+_vuACHAYCckQoID(xxpjrMtw74}rdW4oQS+g=ke-d^qPZzo_B zHmS#NTLI*`Z8qSMEzbGX)^AhT{{jSlpM~&l{}rAw#KXvN@>lc∋!2ENhf;bjYVNqRJ1=GWsu$@Cka!^e5`+ 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
    `F=9NXwGbX~hEcm&d#h*@`<)KHF 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 z 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�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%0A|X7h@6@qxC}`+p|JcR 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

    ItX`m*=o z=r7)%q7wmEqp3hf^cv9OC>Ug7B*M2de2sr%7yv#tM2ST3deP4Vad-%OCTSew9CbN0 zhp~b5fxQ;LGiVvc#9JJI3tbCu3p?S@iOBb{M8LsmktYG}2%1-Pgwov_R^}QAX>^|A zx$KVuC)%=Dp9fFT!Yo@zc_svIn(<&jXMZttgfD>r4P3zC-VN?fUA-f^d&S@?ZH)0h z%{0UIjzZlO)mbfE@l~BC&1fIt29=uD3-WD(3>knwQ~J8`t7LWk5=nOLILW-~Ws)bA zk0hZLHVLSFsWhgnOj=mVl)WikFLRb&mF1P)l&Q+M$aYqeWZSB5N?U4Gl3NX)lCRBH z5lvXr#uuGzEtW+HRjQ^IPUnC8!@9WU4g;)doC(!&OIabKy=^Zro31k7nD z@^v+cp`)5!B1FwBl&obEW^5}H2N1u)6J-j*f66){MZJ$?)^?FhJuI?ceBF9gn zMiZvdCJ{E#;|Mny!}u!ZC%lvO51zw;Ly|ib`ue&yWGAfw7#G%o!Oolnvx+`$9dbm z_E%<#iaKyoVbb$up*>J(Wmkf@q;q~7S+iGI+wriax?R|eXb)<-s(R3ntxBt>tH^a2 zRbnk(^`M5TnqEUuEvWHP<<}^b`)aQ#Z`84s@P>;DZDXw*$Jfa23)-Yx+TKadNmh&7 z<^H0r?QdFnofCxKy1|xyeLlZy;7PN>^0TSR4r+Sny45(_b4Q~J@VId_gweFx&($;y z7Rk3E3R)JU9taO&>f7ewCQ5qnV0kLhp`1=4t2yK)+H&$&T^t2w_>Zz`-~r`@`8lP` zdV`Yd*hksrnny9ZV<~Je90ddbP;LP|C`}+3M8m1ny#_0LC(>d!tx;yp*iJp3m6{o(nlz4;M%3aW;_bX$j2n936DqYk82! zdw$S%AU$Xq_($MlA6#Ite+Fj&7RT;G_A>)8yBO2(CA1qP358EBA=w%G2^O|D?q|>{ zbX4$TENNnUO;Kj)KUMnLx?(Y#M=ZWx2n;`U^B`5fqk;whl$KZ7I zHZkILqo|*>>xr{9JF%>eH31COEZ8K)WZ&nqWgwpPk(Wq}bAJ{!J3qBaZH=v|gNW84 z(*_~YC==Z4Unw}y=VtT z6kldqz`t+4%8#{l^6y&~wbWY7ErphEf{m6Z!d7!ZtJ?$-sf{PZmj>dcUj0P*L_@T4 zbML|SLprPaWY<{~d zrAMAZO^=R4IAe0)1hSLPB2wo=Q&S0Zm3jj7+fh*4Mewv>%&?#T{DF!&3!?M%3I)+ zpKkdhWwyX1P5e&LW&Rk^LOxOy#h)$;;P;B0&8=cXvrE$3JXJQ>{9TUV)0C8vAo##cWMu7gSp5?QyW|x0w?RL1O zJ}uyKlN9~AB@H*NZ7pGmbQ-BnIY>UISxh~yJ3)KVw}~z?6B!W46$ahi%J2mYFup=G z3}0vg!vndWaRSX_T*1}T$;2IW7MVnUPHCV$qh6y0(l*fIXp3nQ>N*;4Bzqc0X{I?y z5%lN8=k(EpaE1(5#<-4M%iN8DvTmbuSYUJ(n~$ntgVC{^x9I(xznEK`>A2&ZS%d^m z1u2L9l&WQk7!u}U&N&8!XQF|^a8!>d8_61bhtQnp#_mlCNAt%-A!{;xVUsh9eKWFG zfgg+?^qxN9xW|kMDA(=rAMK3nC4+tAcqU*5wtwU3P<>?5Hr=u~jTRbtNi#ioQ%4f} zh)PSTR&2s$%exU4>2}{H2?HP+=@wYTGwa_+V*M+MKPK_@&D9wq*prnWQpxXigU{H5wg!yy;QoP(keZ&%*%%&+(gSWuA%K34I_cU0vP=+P<@d{ND;fRefc z=&y|%vD6kS-r0JUkSMJpmMDu!XEevi+jTyajeScgi_K>#(e{g!7S}dPxK}bI8Z=0* z_IW|>_n%I_26vNkkUvOis6(VI^gPmE^mtM}dNwHm{U2#O>JQ0=q>~pR?vaneA}B}w z+bCOnuThsn=F&ETnDo~G13l3D3&Y{Lo*Com&ysswXMsE&tk0f_Y_-=!_5%Qy-3Dr8 zUGf>t`sTl#nT%M=K%wdMApAq>GO~jFhTcQ$Vwd0nybV}Qs1CIwG81_#<`^tB{+0veg(lOmPB>CDdaeZgH zcu(hJ(YsE7XsLEn+sQ6PtFLbU$ZS$3)akbf3H>g3&-9p_K z{eA7Meosxd>3)0W;8A6!y-=Puv|JY9-Y@m@f=TNETuZ}XzNfLbnzISek=CIz#-fm%St@n&LWf!6%)pLWD)=8{hfFkgd)*=){-Xs{UiaQ zAaXivA$b^Y;lsPaiH3eEht@8Vi=Hr`AheHUAKA;z~(A&bi^|@s6~X`>GWFXyy3kRf=^@ zUW&Vo2t|9tAw^n4sxr5JlxkA_iuU2UrycydE_Fj4qtjBCq|L5pcU9ImbscPo?OxM3 zw)o2Z73|rIi0oV_hz3yY;5TVayWWF(5>hy1~}7TB}g0#t3WLrwh_luUk@; z^p;PGeEt>1ZhnnoE`OSGA>T)Jir=Xc^PAe|wd8dSx13kk3F0;7!nc~vR*5E3RHS(% zp00_No>A*$YdVS*234+#sjTSmmm4%cCBa=0qTRX^!iL^*eq^6d(~W_}^-hzpX35~` zs$aI_6;Nke*(}%X(zEWml8>IpN`8A6miz>sD7_69l`Zx8S>fkLtiJE>sD;BOHLixc z`417`wgTi7>FFLPzv<;XK z)G|UnWz0wuRT1+cDLQB}@lZ$${$fNtE<0u?=1%-2)Yefakp;>Bz&EF&pa(|h_$o$6 zLavOy2fUGL_UcU@?eTEbA=mf#T*sK0e(R)&5Q`yrlQAif~=QTOmRV#zIcgk!YS7cf*rd$9BQ51omDPKcQwV(2>Rxk2jp=H4` zbW->N{Ym7lelkjDx{pq`g0QDX{^xtwHT+|bFNE3NdBj}cOHv7VKY6210_B~rlyb;# z6Sc!nN0s``qiyy3Mtk84qM!DeN@qeY(T{_w=)VANy3{L%Ve?qcV7hNJHoB@ATIUes zxg(VM&Ay1qu%Bc)Y;T#F_6jD%(ZQE9a9K^G244;$r*w8j+*Oygn&q4AGmQsW`z z>&7#xkfxgUmZk*tz2={qH0^(N4azsYzva&j1=5fG3UQ4wPGmLLw?06^f;#7O0dKfKaKR%>*yokmIs^EtEeHHq?B#2bj`#nl_zzapeg{GCd>ByFeF}X= zKMi|qz=}I+IY>BcR}uep(aEu%sgycEB9#k4(~|reX%6Ui`Z9!_zAoTj1`J)nn1jJE zM`PzQ>#=8A3UELA;v*AnU|?@_Cm`3AP=%{ z@Nwdi&<=cEI1Q(bOu_t)PC!kF#UT&GNnz9D&-nj}r}_She+Hfqj|Z-cJL-jwRk>$I zC%J+muQ=|8>8xLavn&ULUK+jG$o^UM)B62n-=5R>2VG~-X`SZ~mX7=Wg{o@EJp~g798XdfO-6KJkrixpaKj zT19;)vYn@CRllC-fs*Z$a=)a;ReN!>pj3!L0`6(7+U5vBaSok-NK|FFuNQiM26QRRDNY949 zknO`?C;{$oRLAgF+Rxz+^jX9AN0Pz^nR8r|S%2XcP zm67hOjs-4O=SrtX_guTEXN>iQfn!-dfH48gxPgm8pvNf$5DGd@6x(Fd+2XHIasGH~`nDqhUaZ!jGybkI?yyd4PCi$!(@j(|!D*$szU7ml5YupV4+0beH zG)Esc*9ybXEd2pTjSmsZzChS${T07^x_*dLI~lY@^ToToJ=!x-*);rJc5f&{a@(P3 zYp~rAj?abKy#ssAJo z@BOaI*43&fb@gg9JK;U!)Dec6?c)aiQ%*3~$rG)!WKaiDS~?UeneV-1OgB*N^-C%!+ z`#5wNU*|qbDDc`%+z#AK>Hz;shWKuzNd2}_mq7Q@F2Ih`*TGLSZ1D5Ubi^svB*Z>8 z4Y7n%4UY<(2R8=tVGn{P!$P?Y&_~=gP$QQKW%K-?F}z?Xm3I!R=7z&gaEWkt&~(I{ zpl0N`z|Wdo)@mY^38WO!OK8(*7n#+R`J4nYntOxzHMkqk35&)pj@X5n z8ubaKkM2Z<#$pkmxHQ;~xRw5!;|}{;Vy{8`V;_KWq8|XdBCmKAhVS)=51l#u505f5 zCQxkGG0#{x(1I*{(l28;K7AzhS)d<6MD(14-tS8D1$G95w{}biD3rOL^W|&Ya>-KH zKG7p*NGsPd)MB=onw?g3(V|=YMU%CY7SY{)xnl^)m7%W>KW!q)gugV z^?-3fjdNgnZB74`y5)WNhF|)QMsbh6`EPfuV7K;TtA}QRcwKv_ELTBSx}*^uW5qK% zZ?zum1`9^qRL!EkZH*4&iF(LjXRX`zxJKwKsJ<|qR{ej5&a$nIu8YD+CX<=C8%YR| z;BIxNF4X%}sk^(oy>)M?ySodeLJI{76n77CcTe8@f%$N*Yxdb^@3rm~giJ2p37jc@ z2hRJO1Klh+j~!mt4?n5=3*mDmjC|Ig(qBAO(#; zbOv4Y|KR=cJ>V<7X9QU9&fxBzMZ(?gp& z#It<}aVVlvl!V$MI)pZc@uA*f3heaIRzhZoiOd%=>G^`5tW5rR?$@A9KAjsPoW{-# zJ4#Qoxl(N$4ZRGyq@n9%(EMMx>RG_mxym@Vu#scQw{4?7f=LT7R{r znU_i$48iiVx?0s@&0gIi72R}IvCB%3e{vK`KLu!_Ykf&w9SDU40k|cl=%rmlFp1I? z&?wm;_=CI*UZP}Sr>aHRomvF;ul^I9Zkhr=wS3Ko1s_=Qvb zjbUEz-SF3*%&6V&@|bllLHq@0Y@)$oNjm0OnX=MxHZ|KZH;wG9Nvn2h)4E(Q()xI~ zY4zUZ)Hi-_@(<+xBo|PeFbUHV_XqwJGYEe$DwmiL5ltx+?VwE$eaz4W-)9}+FXp5M zNx5b=n^(ki@IKS`@qbg_@>S#`0U8IIzk?9PYsWdcMcxly_z=UQ|GGY8J_6oufDYAzlgKV|3eLFu>oUR z`QXDgA2^}oE@rrd1?`n)L3a87pj)aN&^ql?Xr3VlT41h+R@(6J8s{8%fu|Yn?>~aI zAeZ8Hq4(pfFtr2&ypU9eOQftM#L11wIZ)sdDB%So9Z|r8DksS zzS{z~XiSw2=ZtS^*BCxj?bOT4KkCMp(R9d?V_JU^PdmBrlZO0fh9;%Jr~Z|HNnMyP zRu3w;p-TSaQ=TszqFhqEO7Wm%n|%1c#j=db9@3LFdI`VbRA*~*aL3>FTWw`syw(=Q zrRFXTuc^WCs^N)cR{bbPblnTLu(rgPQF9r2qnd(Fua3sxtGl3x>i*b6)qQY@HEjVc zTs(e4T^zokUV+=%xE!~z`5Sgms{#JqPJ&}4V(6iCASOkz2Fz1^M~~6=LjTn(fD}^= zu)_kP@7Q*uZ#ag6t6bACB+n;kn|BuWt#2s)tA82sBH|l)FtQh|1X<2VMr~y8K=Fg# zqTcZXZ(E@sbu2U-crKa%oDM$;L`Sv*tD;7ur$*PIJE9MQaLhDJVe}LzB6>R<7RW~^ zi0Ff7hgTB>qLUnE^K8&apMIt)Gn*9$$Z~J3H7Wvl)i~L9VMZV2J z<9!*NS6(->+4GS8&pnB{&xI!&9rXmB{WlI_t$}Zu0zp-VV_;n1BqCLjfJ-(3^WL9iNy)B~yPfFea@ZZVkz%v#dR=#2rz6XF$+pV6 z&@#^1%VhP07{($*x(UE2jRU-|>H+sqQgI~39(=6)FyW#shB!7rb)P27BEFLW#F_FX zgi(s?_}$9qIJN2#_K4;toTjIzv= zNxbFikGtp?35~MtKMSqa;5rs%ojU_q9%^cT;y^=G~7-1j5y@NYs-&)hSfs;|`^$JaK`(2o1ccT&8ZtlHg`p(At- zG3B+PZ7*9kJ6oI1d8Rc^@W(d1Le8px2$<^nf~LCFnCbPyq1c8ZXnrFS_B2@`W6KAq zd;55(ptBrvN;(*GSdk0fR1X7-b$YbTn1^OtOVPm&7U*}~2VZ#iVT6ch&=^!SHW_Wk z{Q_Yk2+bo~At&_${EMN6-?4r0&!8%}Rj>tih1emicsk^cc#Fx8CSv}J+XOyJaG{?j zJw{JUIfj0m`W$^BjSceBOTk|04H!dOcX)7`8rv<^jQ^KBjKoTkP#6i{={2#Xthv#s zp!|rj0)qHKNJgkbv>`Y(@+oh4jDZsoKbrL|@edu9vY6VHnna$NE+S@i8-nlZb_T2J zhJjahyM`#svt$bl`R(h^misA>K!;{KF$keAmOWy#>J|JXJxpt{PTvXA5nk z-A&qK?Sr3Tehop!dFaEsnaCFP10PvA#1kRw>xz?{c0{)KwuiU$wIwy4u>MyUWxZRC zw1!m%HW2@YS#_mXtYb?&R_fn*w%)~GZB<1GdqfUq-O`=Y|?Q)yJDQY?CEpJpH9@kre zFSU;`v>F8VbCntQp>hv_Qu&ZrT(O*_s3;>Xt!yBTsyahDT8$!w)^;a?^}PrQjRO4X zW(78+?Kxc0u?;%gH3KtUo&~a0$I)Lj7NC#*3UJbR6nJDV0QOraqnY;Z;7rF7%v7fW z0$tCs%Uo~pM_d}>V%Ind*;PS1;+(_ma2VMl$Ht&p_E!EC8(*lhhKEkGYQij*h2jFs z(eOIUq=*!2ZbYj!KcdyPA|l=KA-v0ZU#xNu5@q<#hsqImg_XcgfddoA%f#hyiikZ} z)2J`#CPp;%0OuVE$)8S`Aq>SG2&2L)!;>-isJ-Y3F(A+@?h7&}{sy8f;iZ3FqSW^w zvA1tY;$v@ag2?+c{=TPMT$pEh%z5|tD80)bp6FU48s>Z*(#_E+u-ahW9&0M6&b)@{ zG8WPl`Vo|eS}(D;T7`eC;Nd*dQ*b}Y7-&=bM9jOEJ0PMd9b8tA0bMmT@Ml#fSXl8E zl8Yv_EOJb%LoM0bPPD6KZuFbrebZXQ+S=? z95GDZkDM(%N14)fhB{BukM^VU25oKUXWGWjY+7;W5ZZD{33XyuI(57BB&A8#MP97v zOJ=H`kQ&tqq{rG?;%5D0Vo&1>Vy8(-TxOX=`eS904K^I5!`_#A&hdl>IWrmEonfpf zXBem1F)-*q$5#Grdrokk%^#9y?IC(;nI3+|JS}poDLQ(sF*oLrffQGur^olw|BTn` zm z@z<&ENc%`!+HU*|=1q7NM+FY#EdvS#BBVvg@_!ExbflzuKnWS&i&#U4zl=) zJxb)XRR#P&+>pSf6THQGi)XdCII}Gwtf%HsdZmd(DK^>&#|?5UOK-x=(~8mS)JKtn zmGS--8QVL!Ym9qWN1gLR>pRDVCYk+6{WAOBn%?%ql@skR{ynh+rD=|H#Y)G@!q3iI z1y=$wc{|k5A7eVJn3Weaphj4RXa*Q)Wp!PvnfQM86iD`z9-3mmUW)S z?&;WuSGRi!Z`y^V`t~l;jE;%qex2*dTO||7IH`xElh46H_B7$5!HFMl z-i@c)67hc=H2e~GH@wVy9p4Qxj*y9(KnzFUC4B$~QQVj~ngN>5xCrO7bnqOm8y4~N zVPEisfIj01^hex?xfrPdGh%e;A8|Us9RR3ml6a{7DRYq@QkxKa(#{|n(oP`0rPU&0 z(>5SQsY_5#lHUV)iPOQ&@spu{F*mSvkqLyWVh<@U)Jr`t2xWG0r*jfmS9v>W6~XN! zP3S^=idJkB{e^`3fo1Nd0o>@a>W$G z4XseO#+0Ydw2xE@Jq5A~L`K&*u(IPAc2`@VUurQ>Ry83Qr48p1VzT^Z=z>27yUAaPNBiSQqkW$#Q@l&) z2?3sVr%S>);5^C;aX`U;Y@`S?fWHJtT+u2tGUp84W!i%N> zVV45X??CQ&WVNa_I#_uwwnbhNN0T3o|0pYpFOVILPnVa+x#V|Z(MmXGn2HefQk@eX zuMHEu)J+Hx8Kw)i7)J)(G<9PiH^(uOEPbf^EgMO@EM0i6Ht3(j-VB~ zBS4u(hT5RIic-q&q54W)sC}L1fx@;$Xi>{z@Im8|z|1)ZT2X6)E2@KWx2lo?SlD2~ zyUGRvTs4`fsya@bS+kLtRfi`k8%7hX&FT0p?SHWgyGZae1%&CX{R()E-I49KgT7Mt zYxhe;kz)x8KnZH`)0eSMPj-hg?)*yR)9O$myrtaBQZZw5PHL+QPZtE#3HR^K{`9^4G}}d1?wxo{+Ljej|ChVqeljWoyC$^~boA zT2@T30gl9*vqhtAb;1f~9q*X;5qmi@i#`V=kpIKx5K z#9m#?YVUzYkC#yQ!*{vrwSPeQPXx2H9T`)M2G0Kxpcmxx!6&~g;O4w5n5v%u^ygp^>8N2~OrHBK!zZ8h{Y zys$pfzH@a`ZwrJ1%K@`Y5A~7m!8b_Gkg7U4)Wl9I-Q2N*5!^YQ`LgpB^MT|L)7hnA z+?DC+R~0vDH7Y7~kajA$UVnsm*p!1GU`6B1_D%3vR|G`y23}?U1K=j)C~!D%7%WHk z!C){eAvuPFT@UrdKYC-c!K8ZKA?ah?Z@Vd7Z@O2x=sj5O>hAB|)^2Y+8`DX?S*d^g@003~N8@{;)zLDH zD!dGPEmT2Z3dEG>0VmTt4CVTDtcJA zN!MCub*il6+Olo)n^)MMHOz4Ishj8wuNmRmSQYQ?U1@aht2pABP@(p`tRQ%AR7ySk z>J6TV+Bfdk^|xIgn+7_g+b-K}ozJXf`9X7*I^I~Rf2|v55on4XdzF!1jcgKfMb`k3 z+}Q|A+GqqR!o@ld=7#YEIK?stEwSwfdOCfm;qH1A*2@Og`@RBu`~~O) zL^5VO!Uioxka1rBazcMUg51NGOEr1UFt)pwvpbwUg3=sa{6n@mLa8M?bgp@i2p=$z zm<*v2VTSyO`+7~pA^q8i68-z|orbC6Y~#)_o;hGSu&x)3w@(YQI(00WCxgyGEFtdz z*5CnXC3Fq94(LWYbWNvG5#a zLHHNt`EaVrDgLbTiEgQ%hJDizLjBq};S@br@Wvns0!$YZ7|`5RgUvp>acDz6MM%LfNUD}!3_N?*q{l=URSkXqZMVidr}=d-8H{R!rahHs3|P3P%7Tj{ir zj((JdU0jk!{sLdEhOy21Bn)6l2D&-;hV!@yK5Jx$Hdt_isg4Sc-@S|R!yiF8 z3N+v|p~sLL_XyZaYWG)A=XzQKo%&Vw5}S|fG<$hQ<5|H-!{=Z~zgifq`zSoFJuMuj zMF|IL>A|bDCHy>X4_>@(9{09xDx0W}VJ_5n(XQ$*Qf}+Vl2+>l1gDOG>#d7~6Sdnh zwQ3R=t5N`I3IgDe?m%sq^g{h@AAmBn97nktS%AH+4zO3}qj8m$;DCP?%##uUJiWL- zc1B?)F1MgJ{%F1jU-tU|;neRULe=jA!nOQ$1XV#X{&Qgo4*I(l`=_)5GL|=k+p2E@ z6Y9f}OPaU%p10p~lcXOU+mt!hR&9YP+9c7BwuNfbT_;ox-foImWVB3#UfJ~pLy%}- zQs)QU=8ho3;0^(CN5?NBvy($2OTtK5U3#KddV<(0#}X~dwS*K+4gRQZ2wr0-$Bi)W z!JW4*!sXhJ;&Pl;++O!9JmmdG814%p_4C(}75)zDFhn=TEJQgg5pj?E%m08M<}VaZ z@wvlRcoQO4dB#Vta8HU`=L%0e?);Ek;IO17J8IG^?IXK?wvXZt0`*Ez1o zGS`UidiRg?KiLXaFwhBrmV;jfC8l1>UgQ|I#V%n0@*j-IxUS4=t_ z{1-PR6ajULHUYsAJrSs=@!kW`g#q^JZO4e%7TbZ?9oF%&+bl0*s?7JIcbn6q)|r<^ z+%u07qb<_V|5-AGA=Y*LT5B0*1n7~)xMQD(*6|ZvBB_C+ZFJrbqO%r zvK6T@mHU&7YkjHunO>jfl;?^H<6$VixwlGRxNAEr-Tm76p6@M(JxiO=-q8(5y&LPu zK1*;cC49ZS`|seGT2WzAoLnw}ItxG`)5GYo$1y9f|g3QiheH zEHpK1CK$dNzG!b+2#1?f|NSEm6rtX&2EZ8-sNZ7RpjYIs4|T_++bYU0T^ zs{fH6RpBYvDit}uaviy;@)c=p)e9o0dK;l8PH%_o&l`qreZ-TXdc!4s+V3gQ6Tp>|tjN;koM=>8cw` zo$NVBZ}ae3{k`Wo+r6rw>t3?pnb#it)_XqWi?<>4uQxZ$hW^GU)I{P-}l z|96N1K^8`!%=}BhwxA*4Gjd^np8WChnkD%)NMQzu5qlal$#%@yYiv!ht;`OTY@qyiJe6Bh;{r{e-ca75W^{L-$81W#u& zvAV-ew6@4x{igU0h0MdZ5K$;%ZB6nDHGvJbuM^K=Rln| zs{M~F*F2?mzSHE|XvO(H8L>#KwiW29+K0)ONnxP`g>P@GBFLM^lLj_!BfIJ{DM7Ur zltLM!BCNVdfvYf-`BfvyG1bdSQ);#lTWWU_-qjz*mo?tS1+}zbkF{lBshutG z)vot&kh}`sqwItIp|)Vlv~JvO{X{~d(MjB3l9E@L88nt<3uA)?%RXZH%$;f3#<_T=(-(rn9H`mu+@Lk%b!-ZJr)2F^0uh469-$8~Vkd3>Tw~`t4C1gDqm2fg0Xm zs0*8BoEp+!!0S!#yN}AaLAH%*d3*r@REOjFkdU$(5tJjpf=X|{CSN`@9KU15lrWqp?cP-Dm0LT1}aQF09gzrw%F~qQjJk->>JLtBWC`?!N6li&M z61=%O2ZjQ(NnR}m`?RhBe%^2h<}`EQ*47)4z9Ss!E6u^YRjkAe(hS4Y>L+1lnr~qK z*y16i3xxficTh5VQ5K*-YWAP%NqLOe=uM~qHCgj|z$1jR_z1O1ZU zf|i6Ts9)R=Tx2wg_&wZ5riMk*lY-Z?VuSv2nXC{2k~TZUPh2Mo#f^;EgsF}q0=^iN zKPN85i%6(&eNU)$m=lKBvl9fiXYn(v$#D)#T#UhTKeC7Qq4<||KOJ7^_Z^Y)NJE)C~z0sWw zyTJU0nV68q5{#|!H)c@tKn$|A9~jgA19;b2h&(Hu>%nW5Isd13SXt&w(>>cR zozQhnwaNQUb{XO9+zQNYgE3HZKN!(K!DZEM#V1s6ChVxh630~}6W^5E33tk86GRnT z@QIbfa6hU#;L@6L(D3^Eps2|RjBCw6ed&0P$dvZ=I~0EJeGTBvFpT$nGaK9(yULAo z_4R!7Xgw^y&5K1&^q)t05w8IiY7UwJK0yBm9YTM>;?dEBe!vM*JgR{rMR;gi{DCXQ zdx*8xQ^9t*ayfUM1GzgLQ@NMyK90|p%E`5PSZ%gh%w_gj^tld*n(tgny5K&6FZCXV z=OGq=nLtm}J`CM&z`~yY5hI*Esk>}ChQM->Lo@ODiwvuTVft-hBXkMj?b@A@a_zL} znYzz0-Srn^*BVr@2-Ex6TC*cYZv7M;;_yXobXA8}c!r1~{0l;6BL@i90DA6la4)L? zL!@)y2V^%qm#_mn7JCtU05cRzMxTT|$aTs@c>v> z=|c9YUXD0W3;0tTD!tvCJ3ZUm`g@d}2KQ>2$8A?G@T}BEd%qgS_^QoC{=2qINP+Vu z(A|x}C_Ja(IA349--jSA^cPZQ`w!CXzHnAI-!)E*w=SsNUCdwRS`u9Ecqr^=-y8D6 zstM_Bk%!n#YeS62c|Y==?JAiUwjC;Ckz5gg<0rt{N3OJ?o3P-Q;rFtaiQ5HHRcI! zDuxDSflmP+a2(MK_0!8iOn2Y%E_61#4+c0Hxed9Hskh3b;k~lz5pGM zCShhqzeB%`x&WMu{E0$E3`RlXSk$x7?I?t>7eM4MM0aug;6)|^PNU7nRgeO%0X&W} z67E4C3I30D6eZ_!5d#E%@6M3no?{}XYjOnJl^=E6Sr&8AnH8sZE{%WWlE=Sum&Rkg z{P=8NRqRBBEjkKyED}QB6t{tWLvKP`1l_Rnx#w_H=2QGN%4!0aP)E1}H4;Vw8whd! z+xVI81vsre7uH(dV^*0ap|=?BA?f-zzCpUvZcsPIF=~IGj#Qq5 zJn9P|QjY64ht3L!St3D5v zmM22bOJ`#Ee??$a(P^OR&nslVKWF_D{|xnV3)@^Li`F?F{UzIGl>IeduJ~k}P$Si^ zY*?aeXc?y6+IddXT|PnMQXkbkG4#_WSXb%RIT?oe-dK|w@yLQi@3m)R?ztYpNM9W8 z5HcJu1TWz~!2iSdA|UZ+$#mQcS}DAPIRH|y_kuF+Ltr_t0J(y1@^|qE`^x$MJjwj~ zZalBnwUE2UHJrWMmB+xkJE#oLXVMMt4E#NR6BLFT7pVAUh>7rWuL|GOl}jFL|3v?6 zDPSKrmGLSJUxa7$>qJ#LNyIgsEn1<=j{Bg`N$?vcBz-nbNUpcMOCD+8l#Fy;Nm6=5 ziF$ufyccye27%cZ*#Wb~C-KulEW|86nv%hJN#!$CG!*3tok&pAC&5qXI<$Hw^se@*uCevwb>GoIA%?VmBgAT6O{ZjiWK&bQ##Ono)#- zs!`;-iU`^^c>$wTMrGfW896%HgrJXd5znNU%)6v&3%VTOs3>$&_GaT;W~!wd4YIE! zNt{gF8&3u}&yPj4A@{m?=wB8rMz6aJ1uOI5)snqfS4%mzt^PSSty+L3my6*;CI2uF zip1c~Kj{FyfQnp^U*tQOzs57WfalWwX|juo^R4C*m3esiEEB#u&KOZY&#<$ZWN@^n z8n#R84LYUIn6A5P?q|xg>aCL;y`7nEis!a(fv-O*3BkmyMUru1R1j$bDum`k;#ld( zLM|3DP;l5cKP1m16jizwg_k(uBJ*vFqG~O)Xr?(f`jYWo)Jnt4$jADKh^hK8@j?CT z&>n`);0?w_I#`*W-!hoUBbZ8j*D?S^UMj7IN%$Vjyb6ebT zf@&8obfD{qILvh^a=j}wCf3c18{pX%|IphbVXS{_LKHGDUI<)>6N42oB4|$(2Kz4j z2X04LFM>9BIZ?ptPZF`8lUVcsWtQB7B*G6TE{EFicEFEq4QLY*e0sFmU4`uKn(sq8 zU%93@ifpNlpQdw;XS(&yE2`hF+p=w*s?LMHfo+utPg5p9Z|DHe)NX?JS37X}%F)E8 ziaF$w6+&vyip{hK6;tTPE8o&Ps`k(?*Lm3z#FE~Hh|9&fwRgtTw1+ifqddSJg@zS~YOquJw17Tc`F zN3ENRk62z8uQT8MJHa%fw6`()UoXSK$~k&p&1apdVUliSYcHLybF=P`oT~q#AsSv9 zR~nDmQp`Qv)2%oCt@h`@XICcF?%jYJfn*aE=s5Co_!Z?S;U0A*1y38s&{8p+Nz|*n zaTHsyfdq#QA^sLG!3RgKz~ZBqLjQ}|2%d<20C3_csOY#qh~Kfz0n^kB|EuT;etJ}) zUmWoRp%?W*?GNpawgkVxjOCxhKHxSGu0eGsIff)qp6u)5fD`n*v67-AK}H^&&h?*&mLV zOVAGKY~%)Mp7*L$=vpW{U@MfP%x{&C^;FGaO_}b6vfLOh_gP$By&P$htLnZ`L^tUbNSy~$Irs&_WXh@+oAk=3qC)_ z{9pb&RCwEv0`8n|qMs;R=ENUklEn;Sc2aQj%<1SwngQPoezF%-*YSij2W zyC_PoKVP*7sMK_WlJ(ng)y8n*8%r`d$DU1Tcg>{IeEX>bkaX%yG>IruV zA8_rYS#SYG4vwd-0yfj}$Zm`}-${njvx_m+O=P6I`qIxhgJ_$b&nQMGl8kbTh)+CX zyv#?zzCgM#Hnbl76!xKh5V|9|)Cc~1%>R6YgC=>~gYO6YAmcpOBByzd#s2gV5)XJu zDK~vP(ujy*-F_nHcYg;Icdr3Acjv*&x^2KcODiX2q=b_DCQhb4i=9os7n#WHEBeGb z6Kn`D$%;9Zj0~=gJb{~mw{a>l@tm(H7mMki&osDK&}TbCsH^P9NFlaQ_+8eI@P6xg zFxIvJwc4KQ@9U&{4!P;h?!E!GZpg>x73k^49B8JVfd8QFM_Q%nMLnYKqNCL!R-3Am z%~q4Sch%Y49h!~YZ0$9UT8C#ZG5lg6O-0nl<|xv5s|KsKMT09GX8#}OJeSdR+S1{^ zraSGqsc?BdcLrijTg;w7K87dIN4W=;NnA^czdL*VS?|dIjj=O+9k9mb`OM_JJ*F>t z9Ao6~3jMHx938o+L%XD8jJ97nPP?@_SnIDprG4EpSGT3}oIYJ1ZY)v3H{M8OiT@~iT%e21K&`~zL6woVqUf}8WHj>=0?irdzZA63TgN};{t~>; z**j#deNyNaD=O@Td01GaNgU=eTo3JS_#EQY?-%wr0KqKdF#bN%jG%*-F&wTvp4G#J zVbpk4)J~+7JQ{;0d2ka6&EzIr1>+v}C-(z4{06fB#cE~!(I0(p%pF#`k(zC@}D)VLd#&5l zee4qXYUj2t&@-k(>08u_KvgxxgI5}+!N2Mj;V0JiCXKB5MtNFYPg`HTk#V*908>d{>-8lLi*&Zez0fxwIgz_@RZ-P& zb#xrGDCR17C?*};=3vUg0jIN`mg;oMW;byRke-t_tgv1Tw zR1icgH90_4r_ZN-Wi?Yq23;f92_BP1hJvIM;G=T#~@VFzw99&lL#h^W0RnTRIK4=&DO3+}OiVJ}) zoDTnLw#s#%+1GY}t~FAr4BaHschyw9Umgv&Nh{Hdx)vZWNz~qTl1(m)glVTrYs}wd zzYJQ1R(n{TrQWOyR^B&ule;X>rBfaIy6(HbN(z0`B=?aMB;n}$k~GZ7t{kYR^aZ?7 zhQR)kKY~XnZ$k=|7CfUl4RqI?MU?1K-eE?qHNx6c`qb)b`(v$c zyl$1 K#J?63$+&zN@>tut*ch&D$3{;9u|H&|Ek^M|JO=Ro!MylUl|-v<@yf_ZXr z@dg=GRvd|G;|Ggh`&A(XGztyE02&{RwptU2i)u6vCBYZ!~&Z(d7W zVslfLJLUA9o@mxte;)fX>K*4G2ylnOUpbXH4VzEA&*GB3j2{$;j-$0xjI z^YnDwczS>MHSH|s6LlcChLVMrk{m!7F)-)D7ocOY0EP+ugHT{74h0wqVaR04djCH< z()W>F>#5{<-Pn*l?g8RC?paZH-9zJsc{&nDdD*E?d}Zk~5%%s=P`i7cLQn5y$L#90 z2P^N%BV6e5k#wZn5Nb*48~UmwBXfJ4i48|R;wFeNJTMsGKjU2D_onCaYl!Rl=iraL z^T3H9qtC(liz-jtK)HZ|Kmu~v)-e&phNU`H= z8LqSD^WNn~4DzOa8#+x_1;uMExZ|2!;y4Y5vPq+)+B5?CU+q1*LbsMa$grJOYWzvr zX3ik>v10K^doc9XaT=B7+T?9?%j_br$C%)&R$F~1rEC2o+HUwG8@Bp~Rbl*vr9*v% zg#)}O{x`dXbSDA;=+oQ~KE7XlA) zeW66cGaQYGCQT)#Qc1)(#sESG3yB}XT@vutAAt<~Jz!MuV!#+Y2{}?Y&z~ZE=6xBQ z;mH^Db?@Rob#(?^cKJ9ow}i#>Tw%QRcB3`=3&@91(}~YP0N)Q=0Y?+dFaf^;_?meN z5CqLb?F~MK%nM^83nFZYbur1vSMhns6-jqcXHp6PYFaqhnf40fNt+4JO5@UIu z&iapt>2*24h}vVAS2cL-v6^svW6dwZx!M}ytGW>+Q3IRQ)!2*J(fk}wY}Yp`R^W3mPH(L!Dkg}=9SM3W-N1Gzd&uXumQz}c%-OIk3D~s2f zS%nr8qhPG5;rD6dn%}<+jlXSrPC+-lxbU>jUQE;NEi2N#uKcduQ0LXYZa$$K)45I` zuXt-n)r~V{SVmgk%o|cuw1gmynvFjj z*#oBv-+^_AlCj01bFgayd=xuB4!*wx{>mc*z++ju%qJQaeL#9xX1(@zFR^N@m~BA((1TU@}-!bR8iCoT2Q#2 zemGRX+$G?%NZeLdCSy8#68V4Zp13SF8*E@f2n#dR{gScMhNq{Sh}7x2FC?FeLr7Gl zVVzPIW?ok*YPDpMUntSLS4cKF#&r>`xzZoTyYd@4qw=l#w1%u)s{bfkVEWRv*vgko zb98ouxJey%y?5J7{WseGhjg|7LA~nO1bps13E;aR;Igz0`JbGF=&tj!n9-U zWc?De%plfho3d3rORIFAmDI7+Hl#Vye!VW+o?7+A9{lgLeal~geM;dXTS@+EtL)cM z%ZEIRDI@QY@njyxaP`+A-Qs+zR{7_>n)!E+s-A#9;2j&=4{h!o!y zCEAHn{?DgnoksTI97j7jE1+0z z2(F!TjUeXKkYM%?%5COE8j^9CK7clZv5r#6ctNUUV2Rrp+4y|=Qd|!040a)vi!CJ+ zuqe_xESWGGhsNE&8=yHv33!sM1_U%V%FozP z5x3k+PR#PHPrmJ+pPGQIO7o)Lr~A>q^eE_A`ZDa7w2%1sR6h|;PNPgr7((O6hBMYg z<}>Gs1Z-Fs%Gn(Bhm+4FaEVj}XFg#TM*=Np-vOAcxBl6TSkFWn*Xbf3waq3Tv7EvM znX{pRrvIV+#1hUYO-9=HdPCh$n|^5ifBI2HD1CJ)QeRLxMUStC42N16MxkW8>9eAj zrH^i;?YQ}_~q3?{CC_4k{zB&oeMo>z!(unkABGuLH80~1)hb?2lU~a0c}(Ta3*#o zx-dQi+?zNO^D1czG%WcCJSdrg%T8K`ACvf+upqvbD2b7fsw3Z#hl__(GDFT$JiL>X z#q3nd1KMtK6>%jA#v+L`(LM1>KOg(c{ThRGc!Bp80y6MpdOzrIx!AgXcBZz{{7SRL zAk|Q`&$P8lm#$Fu-C&ZaO<5g_t?V|TBfQ1wI?>eTozVCJv9)0#fNZD+74-%vrr|dB zS3@1{S>r)`L(_G9Mhg$$*6P9DYVQYc?Nowux1L*Ok#4c> zp7M}h*0stovRz0aQoBQEGmOS`^9)i9K~c|?=Z)rzcAPP&oFPe zgiM)rHe;S~JUv=#qK;Dzp=hL|NmvPy@Ui_g*5BHQk+;}^wJj#Z<(4{cR_j~WyS6j- zQysf4Es|r#6SD8RBgz=fCryE_j(7D1zDcK{MDlvXYsC-WbQRA-)tDW{+F903`lUv!F+sb{^jq=8Jh&^*Qrvdj zy1I#M8(lZrwzI0Aja4qUCYFw{HWn|pgcpr93krS4l0W+mX@zp#e?>jC$$$5%b4sg} zs`4I+7u7#y_WFQ+q-90dkIsh@nj%?pLQ9h{%;N+87`;U9)pY?trmPIYD^dwURXAmz z<~%)4zlB9M6?25v!$IR5Z+Yk3WBK*I#e6CX!%qTx@*-erkQ{fHvyg~nUm_1;ZlZ3Z zyJ+93LWY0; zyi=Gyyc|@|pdtQRPHzvB{lbZ0-nU<;McVA-o>o4w(Za-GtY*wdYd+9wyNp=l*yWw! z+Uq*%d1F`j1lHNeQd2GP*N}<{)z5(|wZm`$W+;B5MoAE9_7F2Q|09-Ztc2^@Jp3!2 z66?{ggfOi@h#mn2_Z{Y3V?;jkA`A#-;n`tYY;HIP8x*||X2lWVTL~`cZc-dfO}PxWru4%iQYpC2DQNtF zWC|fGF@o3>*Msycx(_)#B8uTM56~(<*9j~@(s}<|Q0aic-L4iOR2?=B;WbgU%H=NJ;;d##UoUb9$HP!f= zZMw;5>M}1HXt(M$Lma;=TL$~Pcf9YU#{z|t^+Qmv_bxUw2)`&*|BM9U`d#GNgx5Q~Npsd*pSVN6M9sZ)%}gq>US>(*LSn zW03dDjR$)L<}KYcOJm0y%dNIZE4F2%mGm{-`o5{glH4@ZQrh&3Ij#A+>BHAbBd)c^ zU>3Fytm#A!l!+(lTVx+~70R8u1G+7`OQtuvr}iK8FFmCLr-HW(k(lpIv#?7nVZ;Vo z8Tp7am3GRrgdz50SWA#JHWfI{mSQW|Z-^xJ6-qd34Wok*&MBe)$=^Zsgq|m3BJaWP zqW>H8mr;0r!WQhK#Gz1oQZZPO`~v7mz5@K4Yynb|tHF{a3>KaE61OE@M7SF}lXNRO zhWcw{1|u_k4O=Cs;a%agLzi-Yh#1RW6Fq}j6nBpPI)O}UO8QJ$obr)emP#gfr(PyK zNL@+lNZCYMko=NVn3zmH7*|Ig7QLOaG`x_?4FPFu*mblmv^Dfeu#FC4Co`&1>lml~ zQyCXsI=af5O+RlOLaWksP?oC`$+r|q@UH$|yk9mM8ztKf7^T}$^JJyL1$`KwuK$6{ ztSGfFP$Mh?U88|vc&ke_E7jlGCM#z1L^(qjvB-tpL*o|ry$Djq0 zN>r`n7P7k68pOyR`H%F8J%#dPPQJ3)N>)EN!rFN{tX`xv4UFu2W&BtCgLzWtJ4>Wc zZ7u!w(Wd({-;QgzWqd&qDBP06cMVHdjj)Rw2oYv+)S0k{YS5jtY97x#IdtD+c-AbPOb!w;o)#U z@lIey@eU&X=K6g1IT+6{_Fd;Zrp_Kh7uxnw7TYeuM{H~HMRqE*%W(>w;zA;FJ?nkt zKC_DuJZryD*XfO1MPPFH+2tTiRvA3sgeu}6*a`?iZ29EnT&s_ltGOu z114QFA1T)6`D^s&Tup`pR=X+G5M|w|9%p~k_k+{dGr~Qy!{X6@TjuL*EcfSpToj1; zAPGFJMg~1^J_aza^8%7rv;7sXQhbc68qfOIVea#9r#rXRY_#KR4_ebd-7t&3d^O5j z;tZLhr~1d@L%PiVd)m9|Xsu=dqwTenYBxHux^nN3!CpoE02TGgxEzyap@D-iDF|?F zgXVchL7Ko$@CqsqSPC9Tr{PZ^4e*TMF>27~W!&;4a2B|*{7H^-!2#R7urTZNh;&PN zWP^FaAULlgs?jn(YNYK&nwPBs0oTonT{)oe~bsB9E2kR7HnW6NHb{nN!>({d>z7)t5AQD#s>!AC7usN zhBJe()>eSqZs`XL%tJBHO({sJN$!7aUh3gmUpnx1r)8Bh#Te~bt>gOZRjG(c^08=& zY%~}uA>s~n?;%X>JPaS}Ad~ak(UfJv=@h)sLrxHyNbiN2@aOghe7@){MCcM=l6vgH zlahtrc-aPrynmv(QVHt!Yig9^^jBpD!}Fe>%*mn$R&LvU`^B&GoNvFpcHL;GcaQnJ z)l>bc!$bKj^<+0(_oRN|di2e6+{;_nxejzJb!PUAb(2mZy(_1&~a*3 z4RJPOD`f>WnUP5PG)RyrS1)78T$ujNz7%`zP6Lhn*y zm}D`2g&2=58*F;TN_L};_bv^dknw$7`AZj5HPK$C?Kk@eDh!Xz3v?^&OVud%pNf^f z!v5XCG+7F2U+;WOk;D(I6(b;`cn%~GL(mm52=156#x(TeP^)E8!CC#!yn7VwPLb-e zwN%SDcIY>27aBh(TFr5?INQsf?;ICJ!(2UWBKOU2NnT%5tk2(A=X=(G^`|r#e5)F^ z`PMg9dUKnud#b;Vb=zBCI2$?;j+CBc8%8$KlBHZ{x~IEiC@}l<9gc&#Ilk|-e5-HEoeb$XuZf;k4+@Qg9<5 ziG0Glh(5>tIH+Rx<5#nulXfsl>5FMY2hYBv1O|9j%LqW{q35eWbzzY$zE>?)R=aT1@JdY;%G ze}fbgb%l~MNa#*ur!eBEcbP>55$hcAlwE?z;;isa<&d06&iB?a>_Srrt9RfiBTIjZ zR-((L0J;fCf5^;T;qGqGV@sZ zQ|l{<)1KQI=90GZJk3pFZ`9|xerxT~z})vsf@|MNgZXa|h~}!AU}jZfaBWplVB2fR z|NYwvud!O%Jz5ISL?`#&DQymPp!eobGF1NfkPZQ*%cNk@Vdi~ z1U824LR}FIfIIm$ghXy1sMD!_@u$Us=0 z44pL`MDHuuk8&4GLtQM`h*~sUjGB@68+z}swHRy0F5pet-=H(m0KJS!#YKcK!B6CE zBg|s_NL)$c!&k87FbVwt9!wv=iEb$oZPyTXnJe)34PUV*^-sYZT_NVCb}O=0yC(3v zF5VkD@Z6bcOtyV8pEK3jFau=Q5ly)_L&*zr`mdr!NLk~7 zpr~s)4pEPF%%et#4p3q{{iL&9O~fTVNc=v@T~IA;Mjhxs9T=m;dqOo+?OxqpQ-h&O zx7RFC5o|Sm3mvtC^rGVKbkAhb4X;o5+&8anga6l7p?_AZ)bDG(;$JEx_z!gy_-1tt z_tuN)o;q2JYnbw=6V#1yFimXxSUb_S+mmhmJ$T)63^T=&fSYMq2*0qfsoSkZj2pI3 ztT@Mg4#g$r&hRYc8GV^NVQ?wejdHO|K@_V2ca}Z}{y;HM9>Tvcmg4)^5ERL;M8|~g zLG*_I<}Zm-d&a~(a`nbq9Z>vx`AX4&EiWjs2CH#Pl%F(EMC3<*lFrj)sZ~3U^hNm^p3&b$+%3IL_+G-r_w)?Ma(h@HuIDfG{T>2xK%5t-?H%up>>K4W z$cNdf>I};!-KfDF%vSv|ORt9L*sogRUa#2fd(@v69M`u4Sty%~zA8P7nI|0!?3OM9 z&@wTmPgad)^wUwN206=C4y&h5Sm?dfmgOb4i9AEvl0E*m9QUSnjO(Q6vSVB~U_U0AVJ+=DZ{DeF zHwLsZhIhu(`a8CG-C4I=vpGQ0q@g#c|AbQ1O@uM(qvZGM&$K(515B;lUS) z*F-{v5#TLRiwO;Ja-1sZGWK!W2?);k2&%Fsfk`#}{ zaMY&e2ITB7PDF1*eUQ>n6tI5Y=qqpd$mtdZCe|F0g%3SzQSAPyWBvN06PG0${st3-;_l9D!pR10$_pNJWlU30%!j}#PBHojUyc|b`e)#F_Zq@ zqzFIQt0!zqh$q%Xli>4V9k8FfiNs-uND#?IQbLWScH{(-%UcF7b0iWA%t!I(^@p%W z)$w4gVm*3$Um4x(fSM%*18u8Yi+`vYducb+g3^J6z0KaIt1|lIu{ZH-2maV zBm`F~dkn_PThJKQ1w^-o>%X91<4!U@u^%!A&DX8#49o4Ix^_pDTJ1cexaiv7FL$B) zT3w;CWiE;Irn5wL&hdTU1Un%AYMrLcwxnpvO^5Yz!*`}B11oKEUA5~UZGpd5Q;W>d z@jI(SQq>>bSk_|K!I2B$#4^A9DJGi5bmL-lm37| zlgPL-auNnfp$B(U6z*=ya~nYY!)T^_)1IeHQtHX6eLB)f$tieOcL#yk`5$g%$1Ete z{XC{hco>Op&k5MuA9;?5f{w>s8P>Vt`Nm(RL@!cNIE! ziCR2c+t~qSn;TKrT80j9oekJps=>n63y{CH3yKjQhaR;*1uI0efoEL@(H!walrs)Q5zF zBnDMQ_>napcar}fxIMfK1IJ89H6sL9JM>rLtTYDYRI-eIETNV?C-yHsJ!)H6Z5S`=c*us>E!>R>7g@n1 zFP)THNj;kOAGvHuF6s6VI-E6RHj$GSB+N=75KW0?M0*?;UL6gPR)xor5h2SdW7+>w zOKCycFgT0Bz>Z|1(JU4#@H=a@TgYPA@3P{}L{{a1pHZjXPG7CwOMR$hk`F2Jhyl3= zOH?cd?kjd7zbNPXSE!lpCEBO9e+CHE zhzr8i$mF&$sPU~=QI?jqsHoPL$oAIl2(<8R;AZ=K`{j*vnocK;l` zOL1M@pw`Lv=w`}L2Df;WN#2!dNfR|#l|qj#qV0pduZ8Cr-U2z2T8`MOS{~SBt!u5f zg?h8TBii(~E6WfNN9*s&Xxd8(P~D^nQ-&LUlpnJA`@{~VjPD&G;{+j@5H+ZP!Hm8T z+@1a-gd2+OaF)7@RH}PMb{qU;y0xBs#|e=S`y}u?YJ1F#4|iC$$cx`<6Mz7-g$l)&PmUmgk4EQa(x`1Zi(WuU13;0F63Eg7$+`rIU_Nqol+Nf32sWbiJy{0 z$3`W00}GNl7)jC-R88W0q#-^Lc_h|{*cHV=dcqDPt^8QjdA1+bPm`lTQZEL;H2^T? z4=^@39opvk29X^>h-6VhTMT8;YTY@|pq>uURh8%##a(29q9{08vCC&tY;Y&3A{`d> zEla8{XlNPuPFHKXrm|Tv^3l$-(%(G`#3ujNE)H^_1H!Zj{|CmkDY2;5fAI5KAYw^N zGx1>y3%=RfK*S4!1XKGxd{(CnE9t%i)=Ib-vn&glsYni7P}@C9-3Mp7@w9D}<-9r7 z{-5EsbA}%0$<+3GudA2(*Q=HUR7!lXU6~Nvta1m+)e{4y+Qt5zde}GKc-6DWB6d0K zan4G2iM`%`*7_AGHUr>ZQvg?Nw8Q%iyQsl|ZU)zIoqfQ-;@vQ|^J(S`L6NmiP-8a= z9IlCi)xI$y`w%;L?=Y)4J8^$Ab4U%eK~+i~#_B&nuw*>ycjK2QCBc2Hp5 literal 0 HcmV?d00001 diff --git a/examples/new-api/entities/satellite.go b/examples/new-api/entities/satellite.go index 6953ad32..41a03857 100644 --- a/examples/new-api/entities/satellite.go +++ b/examples/new-api/entities/satellite.go @@ -15,13 +15,15 @@ Thank you for your support! package entities import ( - rl "github.com/gen2brain/raylib-go/raylib" "gomp/examples/new-api/assets" + "gomp/examples/new-api/components" "gomp/examples/new-api/config" "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" "image/color" + + rl "github.com/gen2brain/raylib-go/raylib" ) type CreateSatelliteManagers struct { @@ -34,6 +36,7 @@ type CreateSatelliteManagers struct { RigidBodies *stdcomponents.RigidBodyComponentManager Velocities *stdcomponents.VelocityComponentManager Renderables *stdcomponents.RenderableComponentManager + SoundEffects *components.SoundEffectsComponentManager } func CreateSatellite( @@ -91,5 +94,14 @@ func CreateSatellite( CameraMask: config.MainCameraLayer | config.MinimapCameraLayer, }) + props.SoundEffects.Create(entity, components.SoundEffect{ + Clip: assets.Audio.Get("satellite_main_sound.wav"), + IsLooping: true, + IsPlaying: false, + Volume: 0.05, + Pan: 0.5, + Pitch: 1.0, + }) + return entity } diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index ed649ff3..8bd96b93 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -98,6 +98,7 @@ func (s *AssteroddSystem) Init() { BoxColliders: s.BoxColliders, RigidBodies: s.RigidBodies, Renderables: s.Renderables, + SoundEffects: s.SoundEffects, }, 500, 500, 0) entities.CreateSpaceSpawner(entities.CreateSpaceSpawnerManagers{ From 1b6234b62d2f15655878e43b844eb5d47c08c00c Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 27 Apr 2025 17:07:10 +0300 Subject: [PATCH 183/196] refactor(audio-system): rewrite AudioSystem to use parallel processing style(audio-system): go fmt --- examples/new-api/systems/audio.go | 42 +++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/examples/new-api/systems/audio.go b/examples/new-api/systems/audio.go index c7cf3a1d..e9d97c21 100644 --- a/examples/new-api/systems/audio.go +++ b/examples/new-api/systems/audio.go @@ -15,11 +15,14 @@ Thank you for your support! package systems import ( - "github.com/negrel/assert" "gomp/examples/new-api/components" + "gomp/pkg/core" "gomp/pkg/ecs" + "gomp/pkg/worker" "time" + "github.com/negrel/assert" + rl "github.com/gen2brain/raylib-go/raylib" ) @@ -28,16 +31,25 @@ func NewAudioSystem() AudioSystem { } type AudioSystem struct { - EntityManager *ecs.EntityManager - SoundEffects *components.SoundEffectsComponentManager + EntityManager *ecs.EntityManager + SoundEffects *components.SoundEffectsComponentManager + accSoundEffectsDelete [][]ecs.Entity + numWorkers int + Engine *core.Engine } func (s *AudioSystem) Init() { + s.numWorkers = s.Engine.Pool().NumWorkers() + s.accSoundEffectsDelete = make([][]ecs.Entity, s.numWorkers) rl.InitAudioDevice() } func (s *AudioSystem) Run(dt time.Duration) { - s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { + for a := range s.accSoundEffectsDelete { + s.accSoundEffectsDelete[a] = s.accSoundEffectsDelete[a][:0] + } + + s.SoundEffects.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { soundEffect := s.SoundEffects.GetUnsafe(entity) assert.NotNil(soundEffect) @@ -45,19 +57,19 @@ func (s *AudioSystem) Run(dt time.Duration) { // check if clip is loaded if clip == nil || !rl.IsSoundValid(*clip) { - return true + return } if !soundEffect.IsPlaying { if rl.IsSoundPlaying(*clip) { rl.StopSound(*clip) - return true + return } else { *clip = rl.LoadSoundAlias(*clip) rl.PlaySound(*clip) soundEffect.IsPlaying = true - return true + return } } @@ -67,13 +79,18 @@ func (s *AudioSystem) Run(dt time.Duration) { rl.PlaySound(*clip) } else { // sound is over, remove entity - s.EntityManager.Delete(entity) + s.accSoundEffectsDelete[workerId] = append(s.accSoundEffectsDelete[workerId], entity) + // rl.UnloadSoundAlias(*clip) // TODO: this doesn't work https://github.com/gen2brain/raylib-go/issues/494 } } - - return true }) + + for a := range s.accSoundEffectsDelete { + for _, entity := range s.accSoundEffectsDelete[a] { + s.EntityManager.Delete(entity) + } + } } func (s *AudioSystem) Destroy() { rl.CloseAudioDevice() @@ -92,7 +109,7 @@ type AudioSettingsSystem struct { func (s *AudioSettingsSystem) Init() {} func (s *AudioSettingsSystem) Run(dt time.Duration) { - s.SoundEffects.EachEntity()(func(entity ecs.Entity) bool { + s.SoundEffects.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { soundEffect := s.SoundEffects.GetUnsafe(entity) assert.NotNil(soundEffect) @@ -100,7 +117,7 @@ func (s *AudioSettingsSystem) Run(dt time.Duration) { // check if clip is loaded if clip == nil { - return true + return } spatialSettings := s.SpatialAudio.GetUnsafe(entity) @@ -114,7 +131,6 @@ func (s *AudioSettingsSystem) Run(dt time.Duration) { } rl.SetSoundPitch(*clip, soundEffect.Pitch) - return true }) } func (s *AudioSettingsSystem) Destroy() { From 0b80f39d7ea67df4d9f4878eb8379a01d5f46795 Mon Sep 17 00:00:00 2001 From: bitver Date: Sun, 4 May 2025 13:49:03 +0500 Subject: [PATCH 184/196] lowest --- examples/new-api/systems/render-overlay.go | 41 ++++------------------ 1 file changed, 6 insertions(+), 35 deletions(-) diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index 6c4ea610..5f193099 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -24,6 +24,7 @@ import ( "gomp/stdcomponents" "gomp/vectors" "image/color" + "slices" "time" ) @@ -65,7 +66,7 @@ type RenderOverlaySystem struct { fpsSampleSum int fpsSampleIdx int avgFPS float64 - percentileFPS int + lowestFps int lastFrameDuration time.Duration msHistory []float64 // ms per frame, ring buffer msHistoryIdx int @@ -135,7 +136,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { s.avgFPS = float64(s.fpsSampleSum) / float64(len(s.fpsSamples)) // Calculate 1% FPS (lowest 1% frame in the sample window) - s.percentileFPS = s.calcPercentileFPS(0.01) + s.lowestFps = slices.Min(s.fpsSamples) // Update frame time history (ms) on every frame // Use average of last two frames for smoother graph @@ -316,7 +317,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { return true } -// Draws FPS stats: 1% low, current frame, average, and current FPS +// Draws FPS stats: low, current frame, average, and current FPS func (s *RenderOverlaySystem) drawCustomFPS(x, y int32) { fps := int32(s.currentFPS) @@ -327,7 +328,7 @@ func (s *RenderOverlaySystem) drawCustomFPS(x, y int32) { } avgFPS := int32(s.avgFPS) - percentileFPS := int32(s.percentileFPS) + percentileFPS := int32(s.lowestFps) // Colors fontColor := rl.Lime @@ -349,7 +350,7 @@ func (s *RenderOverlaySystem) drawCustomFPS(x, y int32) { rl.DrawText(fmt.Sprintf("FPS: %d", fps), x, y, fontSize, fontColor) rl.DrawText(fmt.Sprintf("Frame: %.2f ms", frameTimeMs), x, y+fontSize, fontSize, frameTimeColor) rl.DrawText(fmt.Sprintf("Avg %d: %d", fpsAvgSamples, avgFPS), x, y+fontSize*2, fontSize, fontColor) - rl.DrawText(fmt.Sprintf("1%% Low: %d", percentileFPS), x, y+fontSize*3, fontSize, fontColor) + rl.DrawText(fmt.Sprintf("Low: %d", percentileFPS), x, y+fontSize*3, fontSize, fontColor) // Draw ms graph s.drawMsGraph(x+180, y) @@ -447,36 +448,6 @@ func (s *RenderOverlaySystem) drawMsGraph(x, y int32) { ) } -// Calculates the given percentile FPS (e.g., 0.01 for 1% low) -func (s *RenderOverlaySystem) calcPercentileFPS(percentile float64) int { - n := len(s.fpsSamples) - if n == 0 { - return 0 - } - // Copy and sort samples - sorted := make([]int, n) - copy(sorted, s.fpsSamples) - for i := 1; i < n; i++ { - key := sorted[i] - j := i - 1 - for j >= 0 && sorted[j] > key { - sorted[j+1] = sorted[j] - j-- - } - sorted[j+1] = key - } - - // For 1% low, we want the 1st percentile (lowest values) - idx := int(float64(n) * percentile) - if idx < 0 { - idx = 0 - } - if idx >= n { - idx = n - 1 - } - return sorted[idx] -} - func (s *RenderOverlaySystem) intersects(rect1, rect2 vectors.Rectangle) bool { return rect1.X < rect2.X+rect2.Width && rect1.X+rect1.Width > rect2.X && From c75a82ecf169d28fd66e06ffaadb584262508356 Mon Sep 17 00:00:00 2001 From: bitver Date: Sun, 4 May 2025 14:35:49 +0500 Subject: [PATCH 185/196] fastest log2 on wild west --- pkg/ecs/int-log.go | 4 ++-- pkg/ecs/int-log_test.go | 41 +++++++++++++++++++++++++++-------------- pkg/ecs/slice.go | 2 +- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/pkg/ecs/int-log.go b/pkg/ecs/int-log.go index 929a8f82..f47a5773 100644 --- a/pkg/ecs/int-log.go +++ b/pkg/ecs/int-log.go @@ -8,6 +8,6 @@ package ecs import "math/bits" -func FastIntLog2(value int) int { - return bits.Len64(uint64(value)) - 1 +func FastIntLog2(value uint64) int { + return bits.Len64(value) - 1 } diff --git a/pkg/ecs/int-log_test.go b/pkg/ecs/int-log_test.go index 62ebb2f2..ec5d056b 100644 --- a/pkg/ecs/int-log_test.go +++ b/pkg/ecs/int-log_test.go @@ -8,28 +8,41 @@ package ecs import ( "math" + "math/bits" "testing" ) -func TesCalcIndex(t *testing.T) { - for i := 0; i <= 100_000_000; i++ { - value := i>>10 + 1 - want := int(math.Log2(float64(value))) - have := FastIntLog2(value) - if want != have { - t.Fatalf("i: %v, want: %v, got: %v", i, want, have) - } +//func TesCalcIndex(t *testing.T) { +// for i := 0; i <= 100_000_000; i++ { +// value := i>>10 + 1 +// want := uint64(math.Log2(float64(value))) +// have := FastIntLog2(value) +// if want != have { +// t.Fatalf("i: %v, want: %v, got: %v", i, want, have) +// } +// } +//} + +func BenchmarkFastestLog2(b *testing.B) { + var i uint64 = 1 + for b.Loop() { + _ = bits.LeadingZeros64(i/10 + 1) + i++ } } -func BenchmarkFastLog2(t *testing.B) { - for range t.N { - _ = FastIntLog2(t.N/10 + 1) +func BenchmarkFastLog2(b *testing.B) { + var i uint64 = 1 + for b.Loop() { + _ = FastIntLog2(i/10 + 1) + i++ } } -func BenchmarkStdMathLog2(t *testing.B) { - for range t.N { - _ = int(math.Log2(float64(t.N/10 + 1))) +func BenchmarkStdMathLog2(b *testing.B) { + var i uint64 = 1 + for b.Loop() { + _ = math.Log2(float64(i/10 + 1)) + i++ } } diff --git a/pkg/ecs/slice.go b/pkg/ecs/slice.go index 4a3c5785..5cfe0117 100644 --- a/pkg/ecs/slice.go +++ b/pkg/ecs/slice.go @@ -60,7 +60,7 @@ func (a *Slice[T]) Set(index int, value T) *T { func (a *Slice[T]) Append(values ...T) []T { a.data = append(a.data, values...) - disCap := 1 << (FastIntLog2(len(a.data)) + 1) + disCap := 1 << (FastIntLog2(uint64(len(a.data))) + 1) if cap(a.data) > disCap { a.data = a.data[:len(a.data):disCap] } From b7e2a51f4cf250f1324c6af3106ad5cfe6da034b Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 4 May 2025 12:48:15 +0300 Subject: [PATCH 186/196] minor changes --- examples/new-api/systems/asterodd.go | 2 +- pkg/core/engine.go | 5 ++++- pkg/ecs/int-log.go | 4 ++-- pkg/ecs/int-log_test.go | 6 +++--- pkg/ecs/slice.go | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index ed649ff3..a5ffb747 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -67,7 +67,7 @@ func (s *AssteroddSystem) Init() { } stdentities.CreateCollisionGrid(&collisionGridManages, config.DefaultCollisionLayer, 256) stdentities.CreateCollisionGrid(&collisionGridManages, config.PlayerCollisionLayer, 128) - stdentities.CreateCollisionGrid(&collisionGridManages, config.BulletCollisionLayer, 32) + stdentities.CreateCollisionGrid(&collisionGridManages, config.BulletCollisionLayer, 64) stdentities.CreateCollisionGrid(&collisionGridManages, config.EnemyCollisionLayer, 128) stdentities.CreateCollisionGrid(&collisionGridManages, config.WallCollisionLayer, 4096) diff --git a/pkg/core/engine.go b/pkg/core/engine.go index 962b61db..277bc93b 100644 --- a/pkg/core/engine.go +++ b/pkg/core/engine.go @@ -26,7 +26,7 @@ const ( ) func NewEngine(game AnyGame) Engine { - numCpu := max(runtime.NumCPU(), 1) + numCpu := max(runtime.NumCPU()-1, 1) engine := Engine{ Game: game, pool: worker.NewPool(numCpu), @@ -40,6 +40,9 @@ type Engine struct { } func (e *Engine) Run(tickrate uint, framerate uint) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + fixedUpdDuration := time.Second / time.Duration(tickrate) var renderTicker *time.Ticker diff --git a/pkg/ecs/int-log.go b/pkg/ecs/int-log.go index 929a8f82..d1e4e80c 100644 --- a/pkg/ecs/int-log.go +++ b/pkg/ecs/int-log.go @@ -8,6 +8,6 @@ package ecs import "math/bits" -func FastIntLog2(value int) int { - return bits.Len64(uint64(value)) - 1 +func FastIntLog2(value uint) int { + return bits.Len(value) - 1 } diff --git a/pkg/ecs/int-log_test.go b/pkg/ecs/int-log_test.go index 62ebb2f2..d906f9d8 100644 --- a/pkg/ecs/int-log_test.go +++ b/pkg/ecs/int-log_test.go @@ -11,11 +11,11 @@ import ( "testing" ) -func TesCalcIndex(t *testing.T) { +func TestCalcIndex(t *testing.T) { for i := 0; i <= 100_000_000; i++ { value := i>>10 + 1 want := int(math.Log2(float64(value))) - have := FastIntLog2(value) + have := FastIntLog2(uint(value)) if want != have { t.Fatalf("i: %v, want: %v, got: %v", i, want, have) } @@ -24,7 +24,7 @@ func TesCalcIndex(t *testing.T) { func BenchmarkFastLog2(t *testing.B) { for range t.N { - _ = FastIntLog2(t.N/10 + 1) + _ = FastIntLog2(uint(t.N/10 + 1)) } } diff --git a/pkg/ecs/slice.go b/pkg/ecs/slice.go index 4a3c5785..f084bbf6 100644 --- a/pkg/ecs/slice.go +++ b/pkg/ecs/slice.go @@ -60,7 +60,7 @@ func (a *Slice[T]) Set(index int, value T) *T { func (a *Slice[T]) Append(values ...T) []T { a.data = append(a.data, values...) - disCap := 1 << (FastIntLog2(len(a.data)) + 1) + disCap := 1 << (FastIntLog2(uint(len(a.data))) + 1) if cap(a.data) > disCap { a.data = a.data[:len(a.data):disCap] } From ca8bdc8853b8327c6e1f670dc7d471bc5babfbc8 Mon Sep 17 00:00:00 2001 From: bitver Date: Sun, 4 May 2025 14:50:23 +0500 Subject: [PATCH 187/196] fastest log2 on wild west --- pkg/ecs/int-log.go | 4 ++++ pkg/ecs/int-log_test.go | 26 +++++++++++++++----------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/pkg/ecs/int-log.go b/pkg/ecs/int-log.go index f47a5773..42444808 100644 --- a/pkg/ecs/int-log.go +++ b/pkg/ecs/int-log.go @@ -11,3 +11,7 @@ import "math/bits" func FastIntLog2(value uint64) int { return bits.Len64(value) - 1 } + +func FastestIntLog2(value uint64) int { + return bits.LeadingZeros64(value) ^ 63 +} diff --git a/pkg/ecs/int-log_test.go b/pkg/ecs/int-log_test.go index ec5d056b..5085b9be 100644 --- a/pkg/ecs/int-log_test.go +++ b/pkg/ecs/int-log_test.go @@ -12,21 +12,25 @@ import ( "testing" ) -//func TesCalcIndex(t *testing.T) { -// for i := 0; i <= 100_000_000; i++ { -// value := i>>10 + 1 -// want := uint64(math.Log2(float64(value))) -// have := FastIntLog2(value) -// if want != have { -// t.Fatalf("i: %v, want: %v, got: %v", i, want, have) -// } -// } -//} +func TestCalcIndex(t *testing.T) { + for i := uint64(0); i <= 100_000_000; i++ { + var value uint64 = i>>10 + 1 + want := int(math.Log2(float64(value))) + want2 := int(bits.LeadingZeros64(value) ^ 63) + have := bits.Len64(value) - 1 + if want != have { + t.Fatalf("i: %v, want: %v, got: %v", i, want, have) + } + if want2 != have { + t.Fatalf("i: %v, want: %v, got: %v", i, want2, have) + } + } +} func BenchmarkFastestLog2(b *testing.B) { var i uint64 = 1 for b.Loop() { - _ = bits.LeadingZeros64(i/10 + 1) + _ = FastestIntLog2(i/10 + 1) i++ } } From cf54441b051f76f28cd13885bc8c5cdb8ac462a5 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 4 May 2025 13:51:07 +0300 Subject: [PATCH 188/196] feat collision-detection.go --- examples/new-api/game.go | 2 +- examples/new-api/systems/asterodd.go | 8 ++-- examples/new-api/systems/damping.go | 10 +++-- stdsystems/collision-detection.go | 58 ++++++++++++++-------------- stdsystems/collision-setup.go | 3 -- 5 files changed, 41 insertions(+), 40 deletions(-) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 9596e2a5..4340658d 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -106,7 +106,7 @@ func (g *Game) FixedUpdate(dt time.Duration) { systems.Velocity.Run(dt) systems.ColliderSystem.Run(dt) systems.CollisionSetup.Run(dt) - //systems.CollisionDetection.Run(dt) + systems.CollisionDetection.Run(dt) //systems.CollisionDetectionBVH.Run(dt) systems.CollisionResolution.Run(dt) diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index a5ffb747..eff94c1f 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -67,7 +67,7 @@ func (s *AssteroddSystem) Init() { } stdentities.CreateCollisionGrid(&collisionGridManages, config.DefaultCollisionLayer, 256) stdentities.CreateCollisionGrid(&collisionGridManages, config.PlayerCollisionLayer, 128) - stdentities.CreateCollisionGrid(&collisionGridManages, config.BulletCollisionLayer, 64) + stdentities.CreateCollisionGrid(&collisionGridManages, config.BulletCollisionLayer, 16) stdentities.CreateCollisionGrid(&collisionGridManages, config.EnemyCollisionLayer, 128) stdentities.CreateCollisionGrid(&collisionGridManages, config.WallCollisionLayer, 4096) @@ -130,8 +130,8 @@ func (s *AssteroddSystem) Init() { Y: float32(rand.Intn(5000)) + float32(rand.Intn(1000))/10000, } - randVelX := float32(rand.Intn(200)) - 100 + float32(rand.Intn(1000))/10000 - randVelY := float32(rand.Intn(200)) - 100 + float32(rand.Intn(1000))/10000 + //randVelX := float32(rand.Intn(200)) - 100 + float32(rand.Intn(1000))/10000 + //randVelY := float32(rand.Intn(200)) - 100 + float32(rand.Intn(1000))/10000 entities.CreateBullet(entities.CreateBulletManagers{ EntityManager: s.EntityManager, @@ -145,7 +145,7 @@ func (s *AssteroddSystem) Init() { BulletTags: s.BulletTags, Hps: s.Hps, Renderables: s.Renderables, - }, randPos.X, randPos.Y, 0, randVelX, randVelY) + }, randPos.X, randPos.Y, 0, 0, 0) // bug case //entities.CreateBullet(entities.CreateBulletManagers{ diff --git a/examples/new-api/systems/damping.go b/examples/new-api/systems/damping.go index b4b4e67d..7b9e8a9b 100644 --- a/examples/new-api/systems/damping.go +++ b/examples/new-api/systems/damping.go @@ -22,18 +22,20 @@ type DampingSystem struct { RigidBodies *stdcomponents.RigidBodyComponentManager } +const ( + dampingFactor float32 = 0.98 +) + func (s *DampingSystem) Init() {} func (s *DampingSystem) Run(dt time.Duration) { - //dampingFactor := float32(0.98) // Damping factor for velocity - s.Velocities.EachEntity()(func(e ecs.Entity) bool { velocity := s.Velocities.GetUnsafe(e) rigidbody := s.RigidBodies.GetUnsafe(e) if rigidbody != nil && !rigidbody.IsStatic { - //velocity.X *= dampingFactor - //velocity.Y *= dampingFactor + velocity.X *= dampingFactor + velocity.Y *= dampingFactor if velocity.X < 0.1 && velocity.X > -0.1 { velocity.X = 0 } diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index aa11ecc3..ec8fb631 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -7,7 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- Фрея Donated 2 000 RUB Thank you for your support! */ @@ -15,6 +15,7 @@ Thank you for your support! package stdsystems import ( + "github.com/negrel/assert" gjk "gomp/pkg/collision" "gomp/pkg/core" "gomp/pkg/ecs" @@ -61,6 +62,7 @@ func (s *CollisionDetectionSystem) Init() { } s.activeCollisions = make(map[CollisionPair]ecs.Entity) } + func (s *CollisionDetectionSystem) Run(dt time.Duration) { s.CollisionGridComponentManager.EachComponent()(func(grid *stdcomponents.CollisionGrid) bool { s.gridLookup[grid.Layer] = grid @@ -89,6 +91,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { s.collisionEventAcc[i].Reset() } } + func (s *CollisionDetectionSystem) Destroy() { s.collisionEventAcc = nil s.activeCollisions = nil @@ -103,33 +106,32 @@ func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, result []ecs.E return result } } - // - //aabbPtr := s.AABB.GetUnsafe(entityA) - //assert.NotNil(aabbPtr) - //aabb := *aabbPtr - // - //cells := make([]ecs.Entity, 0, 64) - // - //// Iterate through all trees - //for index := range s.gridLookup { - // grid := s.gridLookup[index] - // layer := grid.Layer - // - // // Check if mask includes this layer - // if !colliderA.Mask.HasLayer(layer) { - // continue - // } - // - // // Traverse this BVH tree for potential collisions - // cells = grid.Query(aabb, cells) - //} - // - //for _, cellEntityId := range cells { - // tree := s.BvhTreeComponentManager.GetUnsafe(cellEntityId) - // assert.NotNil(tree) - // - // result = tree.Query(aabb, result) - //} + + aabbPtr := s.AABB.GetUnsafe(entityA) + assert.NotNil(aabbPtr) + aabb := *aabbPtr + + cells := make([]ecs.Entity, 0, 64) + // Iterate through all trees + for index := range s.gridLookup { + grid := s.gridLookup[index] + layer := grid.Layer + + // Check if mask includes this layer + if !colliderA.Mask.HasLayer(layer) { + continue + } + + // Traverse this BVH tree for potential collisions + cells = grid.Query(aabb, cells) + } + + for _, cellEntityId := range cells { + cell := s.CollisionCellComponentManager.GetUnsafe(cellEntityId) + assert.NotNil(cell) + + result = append(result, cell.Members.Members...) + } return result } diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index 1e70747f..e982b061 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -65,9 +65,6 @@ type CollisionSetupSystem struct { memberListPool stdcomponents.MemberListPool } -// для грида для каждого потока мапа ключ хэш значение сущность -// по каждой клетке в многопотоке и проверить по ключу всех жителей? - func (s *CollisionSetupSystem) Init() { s.memberListPool = stdcomponents.NewMemberListPool(s.Engine.Pool()) s.clearCellAccumulator = make([]ecs.PagedArray[ecs.Entity], s.Engine.Pool().NumWorkers()) From 5404d06f243f0a6bb8f738c071470186cf3dbe81 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 4 May 2025 23:14:10 +0300 Subject: [PATCH 189/196] optimizations for collision detection --- examples/new-api/entities/bullet.go | 14 ++ examples/new-api/systems/asterodd.go | 7 +- examples/new-api/systems/spaceship-intents.go | 1 + pkg/collision/gjk.go | 5 +- stdcomponents/colliders.go | 76 ++++--- stdsystems/collision-detection.go | 185 +++++++++++------- 6 files changed, 190 insertions(+), 98 deletions(-) diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index c86b4241..680d1ed4 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -32,6 +32,7 @@ type CreateBulletManagers struct { Scales *stdcomponents.ScaleComponentManager Velocities *stdcomponents.VelocityComponentManager CircleColliders *stdcomponents.CircleColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager RigidBodies *stdcomponents.RigidBodyComponentManager Sprites *stdcomponents.SpriteComponentManager BulletTags *components.BulletTagComponentManager @@ -73,6 +74,19 @@ func CreateBullet( Mask: 1<> 31 + ySign := math.Float32bits(localDir.Y) >> 31 + localSupport := vectors.Vec2{ + X: c.WH.X * (1 - float32(xSign)), // 0 if negative, WH.X otherwise + Y: c.WH.Y * (1 - float32(ySign)), + } - distance := worldVertex.Dot(direction) - if distance > maxDistance { - maxDistance = distance - maxPoint = worldVertex - } + // Apply offset and rotate to world space + vertex := localSupport.Sub(c.Offset) + rotated := vectors.Vec2{ + X: vertex.X*cos - vertex.Y*sin, + Y: vertex.X*sin + vertex.Y*cos, } - return maxPoint.Mul(transform.Scale).Add(transform.Position) + return rotated.Mul(transform.Scale).Add(transform.Position) } type BoxColliderComponentManager = ecs.ComponentManager[BoxCollider] @@ -80,6 +88,10 @@ func NewBoxColliderComponentManager() BoxColliderComponentManager { return ecs.NewComponentManager[BoxCollider](ColliderBoxComponentId) } +// =========================== +// Circle Collider +// =========================== + type CircleCollider struct { Radius float32 Layer CollisionLayer @@ -89,12 +101,24 @@ type CircleCollider struct { } func (c *CircleCollider) GetSupport(direction vectors.Vec2, transform Transform2d) vectors.Vec2 { - angle := direction.Angle() - rotatedRadius := vectors.Vec2{ - X: c.Radius * float32(math.Cos(angle)), - Y: c.Radius * float32(math.Sin(angle)), + var radiusWithOffset vectors.Vec2 + // Handle zero direction to avoid division by zero + if direction.X == 0 && direction.Y == 0 { + // Fallback to a default direction (e.g., right) + defaultDir := vectors.Vec2{X: c.Radius, Y: 0} + radiusWithOffset = defaultDir.Sub(c.Offset).Mul(transform.Scale) + } else { + // Compute scaled direction without trigonometry + mag := float32(math.Hypot(float64(direction.X), float64(direction.Y))) + invMag := c.Radius / mag + scaledDir := vectors.Vec2{ + X: direction.X * invMag, + Y: direction.Y * invMag, + } + // Apply offset, scale, and translation + radiusWithOffset = scaledDir.Sub(c.Offset).Mul(transform.Scale) } - radiusWithOffset := rotatedRadius.Sub(c.Offset).Mul(transform.Scale) + return transform.Position.Add(radiusWithOffset) } @@ -104,6 +128,10 @@ func NewCircleColliderComponentManager() CircleColliderComponentManager { return ecs.NewComponentManager[CircleCollider](ColliderCircleComponentId) } +// =========================== +// Polygon Collider +// =========================== + type PolygonCollider struct { Vertices []vectors.Vec2 Layer CollisionLayer @@ -135,6 +163,10 @@ func NewPolygonColliderComponentManager() PolygonColliderComponentManager { return ecs.NewComponentManager[PolygonCollider](PolygonColliderComponentId) } +// =========================== +// Generic Collider +// =========================== + type GenericCollider struct { Shape ColliderShape Layer CollisionLayer diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index ec8fb631..c2801d1d 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -22,6 +22,8 @@ import ( "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" + "math" + "math/bits" "time" ) @@ -76,7 +78,8 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { } s.GenericCollider.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { - potentialEntities := s.broadPhase(entity, make([]ecs.Entity, 0, 64)) + potentialEntities := make([]ecs.Entity, 0, 64) + potentialEntities = s.broadPhase(entity, potentialEntities) if len(potentialEntities) == 0 { return } @@ -99,104 +102,142 @@ func (s *CollisionDetectionSystem) Destroy() { s.gridLookup = nil } -func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, result []ecs.Entity) []ecs.Entity { +func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, potentialEntities []ecs.Entity) []ecs.Entity { colliderA := s.GenericCollider.GetUnsafe(entityA) - if colliderA.AllowSleep { - if s.ColliderSleepStateComponentManager.Has(entityA) { - return result - } + + // Early exit for sleeping colliders (moved aabb access after sleep check) + if colliderA.AllowSleep && s.ColliderSleepStateComponentManager.Has(entityA) { + return potentialEntities } aabbPtr := s.AABB.GetUnsafe(entityA) assert.NotNil(aabbPtr) - aabb := *aabbPtr - - cells := make([]ecs.Entity, 0, 64) - // Iterate through all trees - for index := range s.gridLookup { - grid := s.gridLookup[index] - layer := grid.Layer - - // Check if mask includes this layer - if !colliderA.Mask.HasLayer(layer) { - continue + bb := *aabbPtr + + // Direct layer bitmask iteration + mask := colliderA.Mask + var cells []ecs.Entity // Reused across queries + + // Iterate only set bits in mask + for mask != 0 { + // Get the least significant raised bit position + layer := stdcomponents.CollisionLayer(bits.TrailingZeros32(uint32(mask))) + mask ^= 1 << layer // Clear the processed bit + + // Direct grid access + grid := s.gridLookup[layer] + assert.NotNil(grid) + + // Reuse cells slice with reset + cells = grid.Query(bb, cells) + for _, cellEntityId := range cells { + cell := s.CollisionCellComponentManager.GetUnsafe(cellEntityId) + assert.NotNil(cell) + potentialEntities = append(potentialEntities, cell.Members.Members...) } - - // Traverse this BVH tree for potential collisions - cells = grid.Query(aabb, cells) + cells = cells[:0] } - for _, cellEntityId := range cells { - cell := s.CollisionCellComponentManager.GetUnsafe(cellEntityId) - assert.NotNil(cell) - - result = append(result, cell.Members.Members...) + // exclude self + for i := len(potentialEntities) - 1; i >= 0; i-- { + if potentialEntities[i] == entityA { + potentialEntities[i] = potentialEntities[len(potentialEntities)-1] + potentialEntities = potentialEntities[:len(potentialEntities)-1] + } } - return result + return potentialEntities } func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEntities []ecs.Entity, workerId worker.WorkerId) { - for _, entityB := range potentialEntities { - if entityA == entityB { - continue - } + posA := s.Positions.GetUnsafe(entityA) + assert.NotNil(posA) + + colliderA := s.GenericCollider.GetUnsafe(entityA) + assert.NotNil(colliderA) + + scaleA := s.Scales.GetUnsafe(entityA) + assert.NotNil(scaleA) + + rotA := s.Rotations.GetUnsafe(entityA) + assert.NotNil(rotA) + + colA := s.getGjkCollider(colliderA, entityA) + + circleA := s.CircleColliders.GetUnsafe(entityA) + // Cache circleA properties if exists + var radiusA float32 + if circleA != nil { + radiusA = circleA.Radius * scaleA.XY.X + } + + transformA := stdcomponents.Transform2d{ + Position: posA.XY, + Rotation: rotA.Angle, + Scale: scaleA.XY, + } - colliderA := s.GenericCollider.GetUnsafe(entityA) + for _, entityB := range potentialEntities { + positionB := s.Positions.GetUnsafe(entityB) + assert.NotNil(positionB) colliderB := s.GenericCollider.GetUnsafe(entityB) - posA := s.Positions.GetUnsafe(entityA) - posB := s.Positions.GetUnsafe(entityB) - scaleA := s.Scales.GetUnsafe(entityA) + assert.NotNil(colliderB) scaleB := s.Scales.GetUnsafe(entityB) - rotA := s.Rotations.GetUnsafe(entityA) - rotB := s.Rotations.GetUnsafe(entityB) - transformA := stdcomponents.Transform2d{ - Position: posA.XY, - Rotation: rotA.Angle, - Scale: scaleA.XY, - } - transformB := stdcomponents.Transform2d{ - Position: posB.XY, - Rotation: rotB.Angle, - Scale: scaleB.XY, - } - - circleA := s.CircleColliders.GetUnsafe(entityA) + assert.NotNil(scaleB) + rotationB := s.Rotations.GetUnsafe(entityB) + assert.NotNil(rotationB) circleB := s.CircleColliders.GetUnsafe(entityB) + + // 1. FAST PATH: Circle-circle collision if circleA != nil && circleB != nil { - radiusA := circleA.Radius * scaleA.XY.X + posB := positionB.XY radiusB := circleB.Radius * scaleB.XY.X - if transformA.Position.Distance(transformB.Position) < radiusA+radiusB { - s.collisionEventAcc[workerId].Append(CollisionEvent{ + + // Vector math with early exit + dx := posB.X - transformA.Position.X + dy := posB.Y - transformA.Position.Y + sqDist := dx*dx + dy*dy + sumRadii := radiusA + radiusB + + if sqDist < sumRadii*sumRadii { + dist := float32(math.Sqrt(float64(sqDist))) + assert.NotZero(dist) + event := CollisionEvent{ entityA: entityA, entityB: entityB, - position: transformA.Position, - normal: transformB.Position.Sub(transformA.Position).Normalize(), - depth: radiusA + radiusB - transformB.Position.Distance(transformA.Position), - }) + position: posA.XY, + normal: vectors.Vec2{ + X: dx / dist, + Y: dy / dist, + }, + depth: sumRadii - dist, + } + s.collisionEventAcc[workerId].Append(event) } continue } - // GJK strategy - colA := s.getGjkCollider(colliderA, entityA) - colB := s.getGjkCollider(colliderB, entityB) - // First detect collision using GJK + // 2. GJK/EPA PATH test := gjk.New() - if !test.CheckCollision(colA, colB, transformA, transformB) { - continue + transformB := stdcomponents.Transform2d{ + Position: positionB.XY, + Rotation: rotationB.Angle, + Scale: scaleB.XY, + } + colB := s.getGjkCollider(colliderB, entityB) + // Detect collision using GJK + if test.CheckCollision(colA, colB, transformA, transformB) { + // If collision detected, get penetration details using EPA + normal, depth := test.EPA(colA, colB, transformA, transformB) + position := posA.XY.Add(positionB.XY.Sub(posA.XY)) + s.collisionEventAcc[workerId].Append(CollisionEvent{ + entityA: entityA, + entityB: entityB, + position: position, + normal: normal, + depth: depth, + }) } - - // If collision detected, get penetration details using EPA - normal, depth := test.EPA(colA, colB, transformA, transformB) - position := posA.XY.Add(posB.XY.Sub(posA.XY)) - s.collisionEventAcc[workerId].Append(CollisionEvent{ - entityA: entityA, - entityB: entityB, - position: position, - normal: normal, - depth: depth, - }) } } func (s *CollisionDetectionSystem) registerCollisionEvents() { From 430d2018ab8a9cf8d35f9a9d4c95f1edae9a2d35 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 5 May 2025 18:42:53 +0300 Subject: [PATCH 190/196] optimizations for collision detection --- examples/new-api/entities/bullet.go | 12 +-- examples/new-api/entities/spaceship.go | 2 +- examples/new-api/main.go | 2 +- examples/new-api/systems/asterodd.go | 5 +- pkg/ecs/component-manager-shared.go | 2 +- pkg/ecs/component-manager.go | 14 +++- pkg/ecs/paged-array.go | 104 +++++++++++++++++++++---- pkg/ecs/paged-map.go | 2 +- stdcomponents/collision-cell.go | 15 +++- stdcomponents/collision-grid.go | 5 +- stdsystems/collision-detection.go | 71 ++++++++++------- stdsystems/collision-setup.go | 8 +- 12 files changed, 181 insertions(+), 61 deletions(-) diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 680d1ed4..e69203ab 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -56,8 +56,8 @@ func CreateBullet( props.Rotations.Create(entity, stdcomponents.Rotation{}.SetFromDegrees(angle)) props.Scales.Create(entity, stdcomponents.Scale{ XY: vectors.Vec2{ - X: 1, - Y: 1, + X: 0.25, + Y: 0.25, }, }) props.Velocities.Create(entity, stdcomponents.Velocity{ @@ -76,12 +76,12 @@ func CreateBullet( }) //props.BoxColliders.Create(entity, stdcomponents.BoxCollider{ // WH: vectors.Vec2{ - // X: 16, - // Y: 16, + // X: 12, + // Y: 12, // }, // Offset: vectors.Vec2{ - // X: 8, - // Y: 8, + // X: 6, + // Y: 6, // }, // Layer: config.BulletCollisionLayer, // Mask: 1<= 0; i-- { j := a.currentPageIndex - i - a.edvpTasks[j].page = a.book[i] - a.edvpTasks[j].f = handler - pool.ProcessGroupTask(&a.edvpTasks[j]) + a.edvTasks[j].page = a.book[i] + a.edvTasks[j].f = handler + pool.ProcessGroupTask(&a.edvTasks[j]) } pool.GroupWait() } -func (a *PagedArray[T]) EachDataParallel(handler func(*T, worker.WorkerId), pool *worker.Pool) { +func (a *PagedArray[T]) ProcessData(handler func(*T, worker.WorkerId), pool *worker.Pool) { assert.NotNil(handler) assert.NotNil(pool) pool.GroupAdd(a.currentPageIndex + 1) for i := a.currentPageIndex; i >= 0; i-- { j := a.currentPageIndex - i - a.edpTasks[j].page = a.book[i] - a.edpTasks[j].f = handler - pool.ProcessGroupTask(&a.edpTasks[j]) + a.edTasks[j].page = a.book[i] + a.edTasks[j].f = handler + pool.ProcessGroupTask(&a.edTasks[j]) + } + pool.GroupWait() +} + +func (a *PagedArray[T]) EachDataValueParallel(handler func(T, worker.WorkerId), pool *worker.Pool) { + assert.NotNil(handler) + assert.NotNil(pool) + + var page *ArrayPage[T] + var book = a.book + + pool.GroupAdd(a.len) + for i := a.currentPageIndex; i >= 0; i-- { + page = book[i] + n := (a.currentPageIndex - i) * pageSize + for j := page.len - 1; j >= 0; j-- { + m := n + (page.len - 1 - j) + a.edvpTasks[m].page = page + a.edvpTasks[m].index = m + a.edvpTasks[m].f = handler + pool.ProcessGroupTask(&a.edvpTasks[m]) + } + } + pool.GroupWait() +} + +func (a *PagedArray[T]) EachDataParallel(handler func(*T, worker.WorkerId), pool *worker.Pool) { + assert.NotNil(handler) + assert.NotNil(pool) + + var page *ArrayPage[T] + var book = a.book + + pool.GroupAdd(a.len) + for i := a.currentPageIndex; i >= 0; i-- { + page = book[i] + n := (a.currentPageIndex - i) * pageSize + for j := page.len - 1; j >= 0; j-- { + m := n + (page.len - 1 - j) + a.edpTasks[m].page = page + a.edpTasks[m].index = m + a.edpTasks[m].f = handler + pool.ProcessGroupTask(&a.edpTasks[m]) + } } pool.GroupWait() } @@ -381,3 +437,25 @@ func (t *EachDataTask[T]) Run(workerId worker.WorkerId) error { } return nil } + +type EachDataValueParallelTask[T any] struct { + f func(T, worker.WorkerId) + page *ArrayPage[T] + index int +} + +func (t *EachDataValueParallelTask[T]) Run(workerId worker.WorkerId) error { + t.f(t.page.data[t.index], workerId) + return nil +} + +type EachDataParallelTask[T any] struct { + f func(*T, worker.WorkerId) + page *ArrayPage[T] + index int +} + +func (t *EachDataParallelTask[T]) Run(workerId worker.WorkerId) error { + t.f(&t.page.data[t.index], workerId) + return nil +} diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 1e02af51..fa4b4445 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -10,7 +10,7 @@ const ( pageSizeShift = 10 pageSize = 1 << pageSizeShift pageSizeMask = pageSize - 1 - initialBookSize = 1 // Starting with a small initial book size + initialBookSize = 32 // Starting with a small initial book size ) type PagedMap[K Entity | SharedComponentInstanceId, V any] struct { diff --git a/stdcomponents/collision-cell.go b/stdcomponents/collision-cell.go index 7d950278..95f2c334 100644 --- a/stdcomponents/collision-cell.go +++ b/stdcomponents/collision-cell.go @@ -23,7 +23,7 @@ import ( ) const ( - MembersPerCellSqrt = 2 + MembersPerCellSqrt = 4 membersPerCell = MembersPerCellSqrt * MembersPerCellSqrt ) @@ -31,11 +31,17 @@ func NewMemberListPool(workerPool *worker.Pool) MemberListPool { return MemberListPool{ pool: sync.Pool{ New: func() any { - return &MemberList{ + list := &MemberList{ Members: make([]ecs.Entity, 0, membersPerCell), Lookup: ecs.NewGenMap[ecs.Entity, int](membersPerCell), InputAcc: make([][]ecs.Entity, workerPool.NumWorkers()), } + + for i := range list.InputAcc { + list.InputAcc[i] = make([]ecs.Entity, 0, membersPerCell) + } + + return list }, }, } @@ -80,7 +86,10 @@ func (ml *MemberList) Delete(member ecs.Entity) { func (ml *MemberList) Reset() { ml.Lookup.Reset() - ml.Members = ml.Members[:0] + ml.Members = ml.Members[:0:membersPerCell] + for i := range ml.InputAcc { + ml.InputAcc[i] = ml.InputAcc[i][:0:membersPerCell] + } } func (ml *MemberList) Has(member ecs.Entity) bool { diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go index bb501184..4de7529f 100644 --- a/stdcomponents/collision-grid.go +++ b/stdcomponents/collision-grid.go @@ -47,7 +47,7 @@ func (i SpatialCellIndex) ToVec2() vectors.Vec2 { } // Query returns the EntityIds of Cells that intersect the AABB -func (g *CollisionGrid) Query(bb AABB, result []ecs.Entity) []ecs.Entity { +func (g *CollisionGrid) Query(bb AABB, result *ecs.PagedArray[ecs.Entity]) { // get spatial index of aabb minSpatialCellIndex := g.GetCellIndex(bb.Min) maxSpatialCellIndex := g.GetCellIndex(bb.Max) @@ -60,10 +60,9 @@ func (g *CollisionGrid) Query(bb AABB, result []ecs.Entity) []ecs.Entity { if !exists { continue } - result = append(result, cellEntity) + result.Append(cellEntity) } } - return result } func (g *CollisionGrid) GetCellIndex(position vectors.Vec2) SpatialCellIndex { diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index c2801d1d..796f275e 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -54,13 +54,16 @@ type CollisionDetectionSystem struct { activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities currentCollisions map[CollisionPair]struct{} gridLookup map[stdcomponents.CollisionLayer]*stdcomponents.CollisionGrid + potentialEntities []ecs.PagedArray[ecs.Entity] } func (s *CollisionDetectionSystem) Init() { s.gridLookup = make(map[stdcomponents.CollisionLayer]*stdcomponents.CollisionGrid) s.collisionEventAcc = make([]ecs.PagedArray[CollisionEvent], s.Engine.Pool().NumWorkers()) - for i := 0; i < len(s.collisionEventAcc); i++ { + s.potentialEntities = make([]ecs.PagedArray[ecs.Entity], s.Engine.Pool().NumWorkers()) + for i := 0; i < s.Engine.Pool().NumWorkers(); i++ { s.collisionEventAcc[i] = ecs.NewPagedArray[CollisionEvent]() + s.potentialEntities[i] = ecs.NewPagedArray[ecs.Entity]() } s.activeCollisions = make(map[CollisionPair]ecs.Entity) } @@ -78,13 +81,13 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { } s.GenericCollider.ProcessEntities(func(entity ecs.Entity, workerId worker.WorkerId) { - potentialEntities := make([]ecs.Entity, 0, 64) - potentialEntities = s.broadPhase(entity, potentialEntities) - if len(potentialEntities) == 0 { + potentialEntities := &s.potentialEntities[workerId] + s.broadPhase(entity, potentialEntities) + if potentialEntities.Len() == 0 { return } - s.narrowPhase(entity, potentialEntities, workerId) + potentialEntities.Reset() }) s.registerCollisionEvents() @@ -102,12 +105,12 @@ func (s *CollisionDetectionSystem) Destroy() { s.gridLookup = nil } -func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, potentialEntities []ecs.Entity) []ecs.Entity { +func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, potentialEntities *ecs.PagedArray[ecs.Entity]) { colliderA := s.GenericCollider.GetUnsafe(entityA) // Early exit for sleeping colliders (moved aabb access after sleep check) if colliderA.AllowSleep && s.ColliderSleepStateComponentManager.Has(entityA) { - return potentialEntities + return } aabbPtr := s.AABB.GetUnsafe(entityA) @@ -116,7 +119,6 @@ func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, potentialEntit // Direct layer bitmask iteration mask := colliderA.Mask - var cells []ecs.Entity // Reused across queries // Iterate only set bits in mask for mask != 0 { @@ -128,28 +130,34 @@ func (s *CollisionDetectionSystem) broadPhase(entityA ecs.Entity, potentialEntit grid := s.gridLookup[layer] assert.NotNil(grid) - // Reuse cells slice with reset - cells = grid.Query(bb, cells) - for _, cellEntityId := range cells { - cell := s.CollisionCellComponentManager.GetUnsafe(cellEntityId) - assert.NotNil(cell) - potentialEntities = append(potentialEntities, cell.Members.Members...) - } - cells = cells[:0] - } + // Query grid + minSpatialCellIndex := grid.GetCellIndex(bb.Min) + maxSpatialCellIndex := grid.GetCellIndex(bb.Max) + // make a list of all spatial indexes that intersect the aabb + // get cells that intersect the aabb by spatial indexes + for i := minSpatialCellIndex.X; i <= maxSpatialCellIndex.X; i++ { + for j := minSpatialCellIndex.Y; j <= maxSpatialCellIndex.Y; j++ { + cellEntity, exists := grid.CellMap.Get(stdcomponents.SpatialCellIndex{X: i, Y: j}) + if !exists { + continue + } + + cell := s.CollisionCellComponentManager.GetUnsafe(cellEntity) + assert.NotNil(cell) + + members := cell.Members.Members - // exclude self - for i := len(potentialEntities) - 1; i >= 0; i-- { - if potentialEntities[i] == entityA { - potentialEntities[i] = potentialEntities[len(potentialEntities)-1] - potentialEntities = potentialEntities[:len(potentialEntities)-1] + for _, entityB := range members { + if entityB != entityA { + potentialEntities.Append(entityB) + } + } + } } } - - return potentialEntities } -func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEntities []ecs.Entity, workerId worker.WorkerId) { +func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEntities *ecs.PagedArray[ecs.Entity], workerId worker.WorkerId) { posA := s.Positions.GetUnsafe(entityA) assert.NotNil(posA) @@ -162,6 +170,9 @@ func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEnti rotA := s.Rotations.GetUnsafe(entityA) assert.NotNil(rotA) + aabbA := s.AABB.GetUnsafe(entityA) + assert.NotNil(aabbA) + colA := s.getGjkCollider(colliderA, entityA) circleA := s.CircleColliders.GetUnsafe(entityA) @@ -177,7 +188,15 @@ func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEnti Scale: scaleA.XY, } - for _, entityB := range potentialEntities { + for entityB := range potentialEntities.EachDataValue() { + aabbB := s.AABB.GetUnsafe(entityB) + assert.NotNil(aabbB) + + if !(aabbA.Max.X >= aabbB.Min.X && aabbA.Min.X <= aabbB.Max.X && + aabbA.Max.Y >= aabbB.Min.Y && aabbA.Min.Y <= aabbB.Max.Y) { + continue + } + positionB := s.Positions.GetUnsafe(entityB) assert.NotNil(positionB) colliderB := s.GenericCollider.GetUnsafe(entityB) diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index e982b061..403552cc 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -188,7 +188,7 @@ func (s *CollisionSetupSystem) setup() { }) // Create requested cells - s.CollisionGridComponentManager.ProcessEntities(func(gridEntity ecs.Entity, workerId worker.WorkerId) { + s.CollisionGridComponentManager.EachEntityParallel(func(gridEntity ecs.Entity, workerId worker.WorkerId) { grid := s.CollisionGridComponentManager.GetUnsafe(gridEntity) assert.NotNil(grid) for i := range grid.CreateCellsAccumulator { @@ -286,7 +286,8 @@ func (s *CollisionSetupSystem) setup() { for i := range members.InputAcc { acc := members.InputAcc[i] for j := range acc { - members.Add(acc[j]) + members.Members = append(members.Members, acc[j]) + members.Lookup.Set(acc[j], len(members.Members)-1) } members.InputAcc[i] = acc[:0] } @@ -300,6 +301,7 @@ func (s *CollisionSetupSystem) setup() { if len(cell.Members.Members) != 0 { return } + cell.Members.Reset() s.memberListPool.Put(cell.Members) cell.Members = nil s.clearCellAccumulator[workerId].Append(cellEntity) @@ -322,7 +324,7 @@ func (s *CollisionSetupSystem) setup() { }) v.Reset() } - s.CollisionGridComponentManager.ProcessComponents(func(grid *stdcomponents.CollisionGrid, workerId worker.WorkerId) { + s.CollisionGridComponentManager.EachComponentParallel(func(grid *stdcomponents.CollisionGrid, workerId worker.WorkerId) { grid.CellMap.Clear() }) } From 2b7ca75ff8daed93fe7c11d81adc05a3f02e3991 Mon Sep 17 00:00:00 2001 From: bitver Date: Tue, 6 May 2025 22:51:14 +0500 Subject: [PATCH 191/196] resize --- examples/new-api/systems/camera-main.go | 20 ++-- examples/new-api/systems/camera-minimap.go | 11 +++ examples/new-api/systems/render-overlay.go | 102 +++++++++++++-------- pkg/ecs/component-bit-table.go | 4 +- stdsystems/os-handler-system.go | 1 + stdsystems/render.go | 5 - 6 files changed, 87 insertions(+), 56 deletions(-) diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index df59d3dd..ca067983 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -111,15 +111,17 @@ func (s *MainCameraSystem) Run(dt time.Duration) { return false }) - //if rl.IsWindowResized() { - // width, height := rl.GetScreenWidth(), rl.GetScreenHeight() - // main := s.Cameras.GetUnsafe(s.mainCamera) - // main.Dst = vectors.Rectangle{X: 0, Y: 0, Width: float32(width), Height: float32(height)} - // - // mini := s.Cameras.GetUnsafe(s.minimapCamera) - // mini.Dst = vectors.Rectangle{X: float32(width) - mini.Dst.Width, Y: float32(height) - mini.Dst.Height, Width: mini.Dst.Width, Height: mini.Dst.Height} - //} - + if rl.IsWindowResized() { + width, height := rl.GetScreenWidth(), rl.GetScreenHeight() + c := s.Cameras.GetUnsafe(s.mainCamera) + c.Dst = vectors.Rectangle{X: 0, Y: 0, Width: float32(width), Height: float32(height)} + c.Camera2D.Offset = rl.Vector2(vectors.Vec2{X: float32(width), Y: float32(height)}.Scale(0.5)) + fb := s.FrameBuffer2D.GetUnsafe(s.mainCamera) + rl.UnloadRenderTexture(fb.Texture) + fb.Texture = rl.LoadRenderTexture(int32(width), int32(height)) + fb.Frame = rl.Rectangle{X: 0, Y: 0, Width: float32(width), Height: float32(height)} + fb.Dst = rl.Rectangle{Width: float32(width), Height: float32(height)} + } } // TODO: check and do better diff --git a/examples/new-api/systems/camera-minimap.go b/examples/new-api/systems/camera-minimap.go index 55e3b432..fc5cc07f 100644 --- a/examples/new-api/systems/camera-minimap.go +++ b/examples/new-api/systems/camera-minimap.go @@ -90,6 +90,17 @@ func (s *MinimapSystem) Run(dt time.Duration) bool { c.Camera2D.Rotation = -float32(rotation.Degrees()) return false }) + if rl.IsWindowResized() { + width, height := rl.GetScreenWidth(), rl.GetScreenHeight() + c := s.Cameras.GetUnsafe(s.minimapCamera) + c.Dst = vectors.Rectangle{X: 0, Y: float32(width) - float32(height)*0.1666666666666667, Width: float32(width) * 0.1666666666666667, Height: float32(height) * 0.1666666666666667} + c.Offset = rl.Vector2(vectors.Vec2{X: float32(width), Y: float32(height)}.Scale(0.5)) + fb := s.FrameBuffer2D.GetUnsafe(s.minimapCamera) + rl.UnloadRenderTexture(fb.Texture) + fb.Texture = rl.LoadRenderTexture(int32(width), int32(height)) + fb.Frame = rl.NewRectangle(0, 0, float32(width), float32(height)) + fb.Dst = rl.Rectangle{Y: float32(height) - float32(height)*0.1666666666666667, Width: float32(width) * 0.1666666666666667, Height: float32(height) * 0.1666666666666667} + } return false } diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index 5f193099..88e43e50 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -81,7 +81,7 @@ func (s *RenderOverlaySystem) Init() { Frame: rl.Rectangle{X: 0, Y: 0, Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)}, Texture: rl.LoadRenderTexture(int32(s.monitorWidth), int32(s.monitorHeight)), Layer: config.DebugLayer, - BlendMode: rl.BlendAlpha, + BlendMode: rl.BlendAlphaPremultiply, Tint: rl.White, Dst: rl.Rectangle{Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)}, }) @@ -121,42 +121,43 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { s.frameCount++ s.lastFrameDuration = dt - // Store current frame FPS in samples - frameFPS := 0 - if dt > 0 { - // Correct calculation: convert duration to frames per second - frameFPS = int(time.Second / dt) - } - s.fpsSampleSum -= s.fpsSamples[s.fpsSampleIdx] - s.fpsSamples[s.fpsSampleIdx] = frameFPS - s.fpsSampleSum += frameFPS - s.fpsSampleIdx = (s.fpsSampleIdx + 1) % len(s.fpsSamples) - - // Calculate average FPS over samples - s.avgFPS = float64(s.fpsSampleSum) / float64(len(s.fpsSamples)) - - // Calculate 1% FPS (lowest 1% frame in the sample window) - s.lowestFps = slices.Min(s.fpsSamples) - - // Update frame time history (ms) on every frame - // Use average of last two frames for smoother graph - var ms float64 - if s.lastFrameDuration > 0 { - ms = float64(s.lastFrameDuration.Microseconds()) / 1000.0 - s.msHistory[s.msHistoryIdx] = ms - s.msHistoryIdx = (s.msHistoryIdx + 1) % len(s.msHistory) - } + { // Store current frame FPS in samples + frameFPS := 0 + if dt > 0 { + // Correct calculation: convert duration to frames per second + frameFPS = int(time.Second / dt) + } + s.fpsSampleSum -= s.fpsSamples[s.fpsSampleIdx] + s.fpsSamples[s.fpsSampleIdx] = frameFPS + s.fpsSampleSum += frameFPS + s.fpsSampleIdx = (s.fpsSampleIdx + 1) % len(s.fpsSamples) + + // Calculate average FPS over samples + s.avgFPS = float64(s.fpsSampleSum) / float64(len(s.fpsSamples)) + + // Calculate 1% FPS (lowest 1% frame in the sample window) + s.lowestFps = slices.Min(s.fpsSamples) + + // Update frame time history (ms) on every frame + // Use average of last two frames for smoother graph + var ms float64 + if s.lastFrameDuration > 0 { + ms = float64(s.lastFrameDuration.Microseconds()) / 1000.0 + s.msHistory[s.msHistoryIdx] = ms + s.msHistoryIdx = (s.msHistoryIdx + 1) % len(s.msHistory) + } - if now.Sub(s.lastFPSTime) >= time.Second { - s.currentFPS = s.frameCount - s.frameCount = 0 - s.lastFPSTime = now + if now.Sub(s.lastFPSTime) >= time.Second { + s.currentFPS = s.frameCount + s.frameCount = 0 + s.lastFPSTime = now + } } s.Cameras.EachEntity()(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) - frame := s.FrameBuffer2D.GetUnsafe(entity) - switch frame.Layer { + fb := s.FrameBuffer2D.GetUnsafe(entity) + switch fb.Layer { case config.MainCameraLayer: overlayFrame := s.FrameBuffer2D.GetUnsafe(s.frameBuffer) rl.BeginTextureMode(overlayFrame.Texture) @@ -165,8 +166,8 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { // Debug mode: BVH tree and dots if s.debug { rl.BeginMode2D(camera.Camera2D) - cameraRect := camera.Rect() + s.CollisionCells.EachEntity()(func(e ecs.Entity) bool { cell := s.CollisionCells.GetUnsafe(e) assert.NotNil(cell) @@ -266,18 +267,30 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { }) s.Collisions.EachEntity()(func(entity ecs.Entity) bool { pos := s.Positions.GetUnsafe(entity) - rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) + rec := vectors.Rectangle{ + X: pos.XY.X - 8, + Y: pos.XY.Y - 8, + Width: 16, + Height: 16, + } + if s.intersects(cameraRect, rec) { + rl.DrawRectangleRec(rl.Rectangle(rec), rl.Red) + } return true }) s.Textures.EachComponent()(func(r *stdcomponents.RLTexturePro) bool { - rl.DrawRectanglePro(rl.Rectangle{ + rec := vectors.Rectangle{ X: r.Dest.X - 2, Y: r.Dest.Y - 2, Width: 4, Height: 4, - }, rl.Vector2{}, r.Rotation, rl.Red) + } + if s.intersects(cameraRect, rec) { + rl.DrawRectanglePro(rl.Rectangle(rec), rl.Vector2{}, r.Rotation, rl.Red) + } return true }) + rl.EndMode2D() } @@ -307,13 +320,23 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { rl.EndTextureMode() case config.MinimapCameraLayer: - rl.BeginTextureMode(frame.Texture) - rl.DrawRectangleLines(1, 1, frame.Texture.Texture.Width-1, frame.Texture.Texture.Height-1, rl.Green) + rl.BeginTextureMode(fb.Texture) + rl.DrawRectangleLines(2, 2, fb.Texture.Texture.Width-2, fb.Texture.Texture.Height-2, rl.Green) rl.EndTextureMode() } return true }) + + if rl.IsWindowResized() { + s.monitorWidth = rl.GetScreenWidth() + s.monitorHeight = rl.GetScreenHeight() + fb := s.FrameBuffer2D.GetUnsafe(s.frameBuffer) + rl.UnloadRenderTexture(fb.Texture) + fb.Texture = rl.LoadRenderTexture(int32(s.monitorWidth), int32(s.monitorHeight)) + fb.Frame = rl.Rectangle{X: 0, Y: 0, Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)} + fb.Dst = rl.Rectangle{Width: float32(s.monitorWidth), Height: float32(s.monitorHeight)} + } return true } @@ -328,7 +351,6 @@ func (s *RenderOverlaySystem) drawCustomFPS(x, y int32) { } avgFPS := int32(s.avgFPS) - percentileFPS := int32(s.lowestFps) // Colors fontColor := rl.Lime @@ -350,7 +372,7 @@ func (s *RenderOverlaySystem) drawCustomFPS(x, y int32) { rl.DrawText(fmt.Sprintf("FPS: %d", fps), x, y, fontSize, fontColor) rl.DrawText(fmt.Sprintf("Frame: %.2f ms", frameTimeMs), x, y+fontSize, fontSize, frameTimeColor) rl.DrawText(fmt.Sprintf("Avg %d: %d", fpsAvgSamples, avgFPS), x, y+fontSize*2, fontSize, fontColor) - rl.DrawText(fmt.Sprintf("Low: %d", percentileFPS), x, y+fontSize*3, fontSize, fontColor) + rl.DrawText(fmt.Sprintf("Low: %d", s.lowestFps), x, y+fontSize*3, fontSize, fontColor) // Draw ms graph s.drawMsGraph(x+180, y) diff --git a/pkg/ecs/component-bit-table.go b/pkg/ecs/component-bit-table.go index 2802663f..ef7ab94a 100644 --- a/pkg/ecs/component-bit-table.go +++ b/pkg/ecs/component-bit-table.go @@ -122,8 +122,8 @@ func (b *ComponentBitTable) AllSet(entity Entity, yield func(ComponentId) bool) return } pageId, bitsetId := b.getPageIDAndBitsetIndex(bitsId) - for i := 0; i < b.bitsetSize; i++ { - set := b.bitsetsBook[pageId][bitsetId+i] + bitset := b.bitsetsBook[pageId][bitsetId : bitsetId+b.bitsetSize] + for i, set := range bitset { j := 0 for set != 0 { if set&1 == 1 { diff --git a/stdsystems/os-handler-system.go b/stdsystems/os-handler-system.go index 4a99dd42..eecb8fdc 100644 --- a/stdsystems/os-handler-system.go +++ b/stdsystems/os-handler-system.go @@ -19,6 +19,7 @@ type OSHandlerSystem struct{} func (s *OSHandlerSystem) Init() { // TODO: pass parameters, resize or reinit. + rl.SetConfigFlags(rl.FlagWindowResizable) rl.InitWindow(1280, 720, "GOMP") } diff --git a/stdsystems/render.go b/stdsystems/render.go index 281a9810..496acb36 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -33,14 +33,9 @@ type RenderSystem struct { renderTextures []rl.RenderTexture2D frames []stdcomponents.FrameBuffer2D - - monitorWidth int - monitorHeight int } func (s *RenderSystem) Init() { - s.monitorWidth = rl.GetScreenWidth() - s.monitorHeight = rl.GetScreenHeight() s.renderTextures = make([]rl.RenderTexture2D, 0, s.FrameBuffer2D.Len()) s.frames = make([]stdcomponents.FrameBuffer2D, 0, s.FrameBuffer2D.Len()) } From f5d9ed9153b55a536fc4dc7484a682f38511d431 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 8 May 2025 13:27:18 +0300 Subject: [PATCH 192/196] minor chores --- examples/new-api/systems/render-overlay.go | 4 ++-- stdcomponents/collision-grid.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index 88e43e50..a7049979 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -318,13 +318,13 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { return false }) rl.EndTextureMode() - case config.MinimapCameraLayer: rl.BeginTextureMode(fb.Texture) rl.DrawRectangleLines(2, 2, fb.Texture.Texture.Width-2, fb.Texture.Texture.Height-2, rl.Green) rl.EndTextureMode() + default: + panic("not implemented") } - return true }) diff --git a/stdcomponents/collision-grid.go b/stdcomponents/collision-grid.go index 4de7529f..563f4a5e 100644 --- a/stdcomponents/collision-grid.go +++ b/stdcomponents/collision-grid.go @@ -30,12 +30,14 @@ func NewCollisionGrid(collisionLayer CollisionLayer, cellSize float32) Collision return g } +// CollisionGrid is a grid of cells that can be used for collision detection type CollisionGrid struct { Layer CollisionLayer // Layer of the grid CellSize float32 // Size of a cell CreateCellsAccumulator []ecs.GenMap[SpatialCellIndex, struct{}] CellMap ecs.GenMap[SpatialCellIndex, ecs.Entity] + // TODO: add chunks with fixed number of cells (32 x 32) } type SpatialCellIndex struct { From fe01bd88dc5bff56340ca2aa3d16695c1b6464ba Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 14 May 2025 19:50:17 +0300 Subject: [PATCH 193/196] init internal wasyan --- go.mod | 32 ++++-- go.sum | 55 ++++++++++ internal/wasyan-server/main.go | 178 +++++++++++++++++++++++++++++++ internal/wasyan-wasm/bindings.go | 27 +++++ internal/wasyan-wasm/main.go | 27 +++++ internal/wasyan/shared.go | 41 +++++++ taskfile.yml | 11 ++ 7 files changed, 365 insertions(+), 6 deletions(-) create mode 100644 internal/wasyan-server/main.go create mode 100644 internal/wasyan-wasm/bindings.go create mode 100644 internal/wasyan-wasm/main.go create mode 100644 internal/wasyan/shared.go diff --git a/go.mod b/go.mod index 56714241..49e644d3 100644 --- a/go.mod +++ b/go.mod @@ -26,22 +26,42 @@ require ( require ( github.com/Zyko0/go-sdl3 v0.0.0-20250324113244-771f317184f7 // indirect github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a // indirect + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/bytecodealliance/wasmtime-go v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v1.5.8 // indirect github.com/felixge/fgprof v0.9.5 // indirect github.com/francoispqt/gojay v1.2.13 // indirect + github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/gofiber/contrib/websocket v1.3.4 // indirect + github.com/gofiber/fiber/v2 v2.52.6 // indirect + github.com/gofiber/fiber/v3 v3.0.0-beta.4 // indirect + github.com/gofiber/schema v1.3.0 // indirect + github.com/gofiber/utils/v2 v2.0.0-beta.8 // indirect github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/hajimehoshi/go-steamworks v0.0.0-20241112125913-96b2a6baef69 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/paulmach/orb v0.11.1 // indirect + github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect + github.com/tetratelabs/wazero v1.9.0 // indirect + github.com/tinylib/msgp v1.2.5 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.62.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + github.com/x448/float16 v0.8.4 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect golang.org/x/mod v0.21.0 // indirect @@ -59,10 +79,10 @@ require ( github.com/jezek/xgb v1.1.1 // indirect github.com/labstack/echo/v4 v4.13.3 github.com/mattn/go-isatty v0.0.20 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect google.golang.org/protobuf v1.36.1 ) diff --git a/go.sum b/go.sum index 1d9febfb..d3e3e6b3 100644 --- a/go.sum +++ b/go.sum @@ -12,10 +12,14 @@ github.com/Zyko0/go-sdl3 v0.0.0-20250324113244-771f317184f7 h1:jlrgDnYn7sUJqiKp5 github.com/Zyko0/go-sdl3 v0.0.0-20250324113244-771f317184f7/go.mod h1:y9Xormjz/GWcAeGbl+Z2zOaKtKfeeZ+5x8UiojyFFdg= github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a h1:N5RnQ3/z1/HjCivGNfRbID/3QreOQ6Dulr3FAOgULqc= github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a/go.mod h1:yJn8avSZmgophSwHMUDhlEIzdkB9uUTEctszmQmjxDo= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bytecodealliance/wasmtime-go v1.0.0 h1:9u9gqaUiaJeN5IoD1L7egD8atOnTGyJcNp8BhkL9cUU= +github.com/bytecodealliance/wasmtime-go v1.0.0/go.mod h1:jjlqQbWUfVSbehpErw3UoWFndBXRRMvfikYH6KsCwOg= github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= @@ -41,12 +45,16 @@ github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b h1:/KAOJuXR4cWaQIiA9xBMDSQJ1JXq5gZHdSK8prrtUqQ= github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/fasthttp/websocket v1.5.8 h1:k5DpirKkftIF/w1R8ZzjSgARJrs54Je9YJK37DL/Ah8= +github.com/fasthttp/websocket v1.5.8/go.mod h1:d08g8WaT6nnyvg9uMm8K9zMYyDjfKyj3170AtPRuVU0= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= +github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5 h1:k8ZAxLgb/p5TvCi5VHFHM8JdnjwShNK4A0bLIwbktAU= github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -59,6 +67,16 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4 github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/gofiber/contrib/websocket v1.3.4 h1:tWeBdbJ8q0WFQXariLN4dBIbGH9KBU75s0s7YXplOSg= +github.com/gofiber/contrib/websocket v1.3.4/go.mod h1:kTFBPC6YENCnKfKx0BoOFjgXxdz7E85/STdkmZPEmPs= +github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= +github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/gofiber/fiber/v3 v3.0.0-beta.4 h1:KzDSavvhG7m81NIsmnu5l3ZDbVS4feCidl4xlIfu6V0= +github.com/gofiber/fiber/v3 v3.0.0-beta.4/go.mod h1:/WFUoHRkZEsGHyy2+fYcdqi109IVOFbVwxv1n1RU+kk= +github.com/gofiber/schema v1.3.0 h1:K3F3wYzAY+aivfCCEHPufCthu5/13r/lzp1nuk6mr3Q= +github.com/gofiber/schema v1.3.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c= +github.com/gofiber/utils/v2 v2.0.0-beta.8 h1:ZifwbHZqZO3YJsx1ZhDsWnPjaQ7C0YD20LHt+DQeXOU= +github.com/gofiber/utils/v2 v2.0.0-beta.8/go.mod h1:1lCBo9vEF4RFEtTgWntipnaScJZQiM8rrsYycLZ4n9c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -87,6 +105,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -121,6 +141,8 @@ github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345/go.mod h1 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -138,9 +160,13 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -161,6 +187,8 @@ github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzb github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -175,7 +203,13 @@ github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 h1:mvIS9aGirkzuYmH github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7/go.mod h1:EbI+KMbALSVE2s0YFOQpR4uj66zBh9ter5P4CBMSuvA= github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8= +github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sevenNt/echo-pprof v0.1.1-0.20230131020615-4dd36891e14b h1:IXGKwQZ6+llGbDFyTJvBXWGTkfrAqsbYwtVVm+Ax4WU= github.com/sevenNt/echo-pprof v0.1.1-0.20230131020615-4dd36891e14b/go.mod h1:ArUb+H7+Tew7UUjK6x2xiAqFrznLrANIfz9M6m66J0c= @@ -212,18 +246,29 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= +github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= +github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.62.0 h1:8dKRBX/y2rCzyc6903Zu1+3qN0H/d2MsxPPmVNamiH0= +github.com/valyala/fasthttp v1.62.0/go.mod h1:FCINgr4GKdKqV8Q0xv8b+UxPV+H/O5nNFo3D+r54Htg= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/U= github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yohamta/donburi v1.15.7 h1:so/vHf1L133d0SFVrCUzMMueh2ko39wRkrcpNLdzvz8= github.com/yohamta/donburi v1.15.7/go.mod h1:FdjU9hpwAsAs1qRvqsSTJimPJ0dipvdnr9hMJXYc1Rk= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= @@ -243,6 +288,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= @@ -270,6 +317,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -285,6 +334,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -301,6 +352,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -309,6 +362,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= diff --git a/internal/wasyan-server/main.go b/internal/wasyan-server/main.go new file mode 100644 index 00000000..c0b2c980 --- /dev/null +++ b/internal/wasyan-server/main.go @@ -0,0 +1,178 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "context" + "errors" + "fmt" + "github.com/gofiber/fiber/v2" + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" + "gomp/internal/wasyan" + "gomp/vectors" + "log" + "os" + "strconv" + "sync" + "sync/atomic" + "time" + "unsafe" +) + +type NodeInstance struct { + Module api.Module +} + +type NodeManager struct { + Instances [2]NodeInstance + LastUsedInstanceIndex atomic.Int32 + config wazero.ModuleConfig + runtime wazero.Runtime + mx sync.Mutex +} + +func (nm *NodeManager) LoadWasm(ctx context.Context, file string) error { + wasm, err := os.ReadFile(file) + if err != nil { + return errors.New("Error opening file:" + err.Error()) + } + + compiledWasm, err := nm.runtime.CompileModule(ctx, wasm) + if err != nil { + return errors.New("Error compiling module:" + err.Error()) + } + + instance1, err := nm.runtime.InstantiateModule(ctx, compiledWasm, nm.config) + if err != nil { + return errors.New("Error creating instance1:" + err.Error()) + } + + instance2, err := nm.runtime.InstantiateModule(ctx, compiledWasm, nm.config) + if err != nil { + return errors.New("Error creating instance2:" + err.Error()) + } + + { + nm.mx.Lock() + defer nm.mx.Unlock() + nm.Instances[0].Module = instance1 + nm.Instances[1].Module = instance2 + nm.LastUsedInstanceIndex.Store(0) + } + + return nil +} + +func main() { + var ctx = context.Background() + var nodeManager = NodeManager{} + var game = wasyan.Game{ + Position: vectors.Vec2{X: 0, Y: 0}, + Velocity: vectors.Vec2{X: 50, Y: 10}, + } + + nodeManager.runtime = wazero.NewRuntime(ctx) + defer nodeManager.runtime.Close(ctx) + + _, err := nodeManager.runtime.NewHostModuleBuilder("env"). + NewFunctionBuilder(). + WithFunc(func(ctx context.Context) uint64 { + return api.EncodeExternref(uintptr(unsafe.Pointer(&game))) + }). + Export("get_game"). + Instantiate(ctx) + if err != nil { + log.Panicln(err) + } + + wasi_snapshot_preview1.MustInstantiate(ctx, nodeManager.runtime) + + // Configure the module to initialize the reactor. + nodeManager.config = wazero.NewModuleConfig().WithStartFunctions("_initialize") + + var app = fiber.New() + + //app.Use(recover.New()) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + app.Get("/update/:file", func(c *fiber.Ctx) error { + file := c.Params("file") + err := nodeManager.LoadWasm(c.Context(), file) + if err != nil { + return c.SendString("Error loading module:\n" + err.Error()) + } + + return c.SendString("Instance updated") + }) + + app.Get("/add", func(c *fiber.Ctx) error { + a, err := strconv.Atoi(c.Query("a")) + if err != nil { + return c.SendString("Error parsing a:" + err.Error()) + } + b, err := strconv.Atoi(c.Query("b")) + if err != nil { + return c.SendString("Error parsing b:" + err.Error()) + } + + start := time.Now() + result := wasyan.Add(int32(a), int32(b)) + duration := time.Since(start).String() + + return c.SendString("Result is " + fmt.Sprint(result) + "\n" + duration) + }) + + app.Get("/call/:name", func(c *fiber.Ctx) error { + name := c.Params("name") + a, err := strconv.Atoi(c.Query("a")) + if err != nil { + return c.SendString("Error parsing a:" + err.Error()) + } + b, err := strconv.Atoi(c.Query("b")) + if err != nil { + return c.SendString("Error parsing b:" + err.Error()) + } + + index := nodeManager.LastUsedInstanceIndex.Add(1) % int32(len(nodeManager.Instances)) + instance := &nodeManager.Instances[index] + module := instance.Module + if module == nil { + return c.SendString("Module not found") + } + + fn := module.ExportedFunction(name) + if fn == nil { + return c.SendString("RPC not found") + } + + start := time.Now() + results, err := fn.Call(c.Context(), api.EncodeI32(int32(a)), api.EncodeI32(int32(b))) + duration := time.Since(start).String() + if err != nil { + return c.SendString("Error calling RPC" + err.Error()) + } + result := (*float32)(unsafe.Pointer(api.DecodeExternref(results[0]))) + game.Velocity.X += *result + + return c.SendString("Result is " + fmt.Sprint(*result) + "\n" + "Game is " + fmt.Sprint(game) + "\n" + duration) + }) + + log.Fatal(app.Listen(":3000")) +} diff --git a/internal/wasyan-wasm/bindings.go b/internal/wasyan-wasm/bindings.go new file mode 100644 index 00000000..b1d763ce --- /dev/null +++ b/internal/wasyan-wasm/bindings.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "gomp/internal/wasyan" + "unsafe" +) + +//go:wasmimport env get_game +func get_game() uint64 + +func GetGame() *wasyan.Game { + return (*wasyan.Game)(unsafe.Pointer(uintptr(get_game()))) +} diff --git a/internal/wasyan-wasm/main.go b/internal/wasyan-wasm/main.go new file mode 100644 index 00000000..fb5c2a1d --- /dev/null +++ b/internal/wasyan-wasm/main.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import "unsafe" + +func main() { + println("main") +} + +//go:wasmexport add +func add(a, b int32) uint64 { + game := GetGame() + return uint64(uintptr(unsafe.Pointer(&game.Velocity.X))) +} diff --git a/internal/wasyan/shared.go b/internal/wasyan/shared.go new file mode 100644 index 00000000..6e423fb6 --- /dev/null +++ b/internal/wasyan/shared.go @@ -0,0 +1,41 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package wasyan + +import ( + "gomp/vectors" + "sync" +) + +func Add(a, b int32) int32 { + var c float64 + c += float64(a) + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + for range 1_000_000 { + c += float64(b) + } + }() + wg.Wait() + + return int32(c) +} + +type Game struct { + Position vectors.Vec2 + Velocity vectors.Vec2 +} diff --git a/taskfile.yml b/taskfile.yml index 33a93063..c0821549 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -101,3 +101,14 @@ tasks: - codesign --timestamp --options=runtime --deep -fs milanrodd-cert -v ./.dist/game.app - xattr -cr ./.dist/game.app - /usr/bin/ditto -c -k --keepParent ./.dist/game.app ./.dist/game.zip + + build-wasm: + env: + GOOS: wasip1 + GOARCH: wasm + watch: true + sources: + - 'internal/wasyan**/*.go' + cmds: + - go build -buildmode=c-shared -o ./hello.wasm ./internal/wasyan-wasm + - curl http://127.0.0.1:3000/update/hello.wasm From f22852ba2ea6b0f3fbadd1381ca8871ead827867 Mon Sep 17 00:00:00 2001 From: bitver Date: Wed, 14 May 2025 09:55:31 +0500 Subject: [PATCH 194/196] revert to old iterators --- examples/new-api/config/config.go | 5 ++ examples/new-api/main.go | 3 +- examples/new-api/systems/asterodd.go | 6 +-- examples/new-api/systems/camera-main.go | 2 +- examples/new-api/systems/camera-minimap.go | 2 +- examples/new-api/systems/collision-handler.go | 2 +- examples/new-api/systems/damping.go | 9 ++-- examples/new-api/systems/debug-info.go | 8 +-- examples/new-api/systems/hp.go | 4 +- examples/new-api/systems/player.go | 6 +-- examples/new-api/systems/render-bogdan.go | 18 +++---- examples/new-api/systems/render-overlay.go | 16 +++--- examples/new-api/systems/space-spawner.go | 2 +- examples/new-api/systems/spaceship-intents.go | 2 +- examples/new-api/systems/spatial-audio.go | 2 +- pkg/ecs/component-manager-shared.go | 8 +-- pkg/ecs/component-manager.go | 10 ++-- pkg/ecs/component-table_bench_test.go | 13 +++++ pkg/ecs/paged-array.go | 51 +++++++++---------- stdcomponents/camera.go | 2 +- stdsystems/animation-spritematrix.go | 2 +- stdsystems/collision-detection-bvh.go | 4 +- stdsystems/collision-detection.go | 6 +-- stdsystems/collision-reslution.go | 8 +-- stdsystems/collision-setup.go | 6 +-- stdsystems/culling.go | 2 +- stdsystems/render-2d-cameras.go | 4 +- stdsystems/render.go | 2 +- stdsystems/sprite-matrix.go | 2 +- stdsystems/ysort.go | 5 +- 30 files changed, 113 insertions(+), 99 deletions(-) diff --git a/examples/new-api/config/config.go b/examples/new-api/config/config.go index abf32ad5..0113c4db 100644 --- a/examples/new-api/config/config.go +++ b/examples/new-api/config/config.go @@ -16,6 +16,11 @@ package config import "gomp/stdcomponents" +const ( + TickRate = 20 + FrameRate = 0 +) + const ( DefaultCollisionLayer stdcomponents.CollisionLayer = iota PlayerCollisionLayer diff --git a/examples/new-api/main.go b/examples/new-api/main.go index 9d28c331..c07cedb3 100644 --- a/examples/new-api/main.go +++ b/examples/new-api/main.go @@ -18,6 +18,7 @@ import ( "github.com/hajimehoshi/go-steamworks" "golang.org/x/text/language" "gomp" + "gomp/examples/new-api/config" "gomp/examples/new-api/scenes" "os" ) @@ -53,5 +54,5 @@ func main() { game := NewGame(&initialScene) engine := gomp.NewEngine(&game) - engine.Run(20, 0) + engine.Run(config.TickRate, config.FrameRate) } diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index 24c356f2..68bfdf84 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -181,7 +181,7 @@ func (s *AssteroddSystem) Init() { s.SceneManager.Create(manager, components.AsteroidSceneManager{}) } func (s *AssteroddSystem) Run(dt time.Duration) { - s.PlayerTags.EachEntity()(func(e ecs.Entity) bool { + s.PlayerTags.EachEntity(func(e ecs.Entity) bool { intents := s.SpaceshipIntents.GetUnsafe(e) intents.MoveUp = false @@ -211,9 +211,9 @@ func (s *AssteroddSystem) Run(dt time.Duration) { return true }) - s.SceneManager.EachEntity()(func(e ecs.Entity) bool { + s.SceneManager.EachEntity(func(e ecs.Entity) bool { sceneManager := s.SceneManager.GetUnsafe(e) - s.PlayerTags.EachEntity()(func(e ecs.Entity) bool { + s.PlayerTags.EachEntity(func(e ecs.Entity) bool { playerHp := s.Hps.GetUnsafe(e) if playerHp == nil { return true diff --git a/examples/new-api/systems/camera-main.go b/examples/new-api/systems/camera-main.go index ca067983..65bd8f9d 100644 --- a/examples/new-api/systems/camera-main.go +++ b/examples/new-api/systems/camera-main.go @@ -95,7 +95,7 @@ func (s *MainCameraSystem) Run(dt time.Duration) { if rl.IsKeyPressed(rl.KeyR) { s.shouldRotate = !s.shouldRotate } - s.Player.EachEntity()(func(entity ecs.Entity) bool { + s.Player.EachEntity(func(entity ecs.Entity) bool { playerPosition := s.Position.GetUnsafe(entity) c := s.Cameras.GetUnsafe(s.mainCamera) //decay := 40.0 // DECAY IS TICKRATE DEPENDENT diff --git a/examples/new-api/systems/camera-minimap.go b/examples/new-api/systems/camera-minimap.go index fc5cc07f..6ca1c197 100644 --- a/examples/new-api/systems/camera-minimap.go +++ b/examples/new-api/systems/camera-minimap.go @@ -81,7 +81,7 @@ func (s *MinimapSystem) Run(dt time.Duration) bool { if s.disabled { return false } - s.Player.EachEntity()(func(entity ecs.Entity) bool { + s.Player.EachEntity(func(entity ecs.Entity) bool { playerPosition := s.Position.GetUnsafe(entity) rotation := s.Rotation.GetUnsafe(entity) diff --git a/examples/new-api/systems/collision-handler.go b/examples/new-api/systems/collision-handler.go index 0db83b12..f1cf620a 100644 --- a/examples/new-api/systems/collision-handler.go +++ b/examples/new-api/systems/collision-handler.go @@ -49,7 +49,7 @@ type CollisionHandlerSystem struct { func (s *CollisionHandlerSystem) Init() {} func (s *CollisionHandlerSystem) Run(dt time.Duration) { - s.Collisions.EachComponent()(func(collision *stdcomponents.Collision) bool { + s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { switch collision.State { case stdcomponents.CollisionStateEnter: if s.checkBulletCollisionEnter(collision.E1, collision.E2) { diff --git a/examples/new-api/systems/damping.go b/examples/new-api/systems/damping.go index 7b9e8a9b..662aaf4c 100644 --- a/examples/new-api/systems/damping.go +++ b/examples/new-api/systems/damping.go @@ -7,7 +7,9 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package systems import ( + "gomp/examples/new-api/config" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "time" ) @@ -29,13 +31,13 @@ const ( func (s *DampingSystem) Init() {} func (s *DampingSystem) Run(dt time.Duration) { - s.Velocities.EachEntity()(func(e ecs.Entity) bool { + s.Velocities.ProcessEntities(func(e ecs.Entity, _ worker.WorkerId) { velocity := s.Velocities.GetUnsafe(e) rigidbody := s.RigidBodies.GetUnsafe(e) if rigidbody != nil && !rigidbody.IsStatic { - velocity.X *= dampingFactor - velocity.Y *= dampingFactor + velocity.X *= dampingFactor / (config.TickRate * float32(dt.Seconds())) + velocity.Y *= dampingFactor / (config.TickRate * float32(dt.Seconds())) if velocity.X < 0.1 && velocity.X > -0.1 { velocity.X = 0 } @@ -43,7 +45,6 @@ func (s *DampingSystem) Run(dt time.Duration) { velocity.Y = 0 } } - return true }) } diff --git a/examples/new-api/systems/debug-info.go b/examples/new-api/systems/debug-info.go index b2651356..126ab105 100644 --- a/examples/new-api/systems/debug-info.go +++ b/examples/new-api/systems/debug-info.go @@ -60,7 +60,7 @@ func (s *DebugInfoSystem) Init() { func (s *DebugInfoSystem) Run(dt time.Duration) bool { if rl.IsKeyPressed(rl.KeyF6) { if !s.debug { - s.BoxColliders.EachEntity()(func(e ecs.Entity) bool { + s.BoxColliders.EachEntity(func(e ecs.Entity) bool { col := s.BoxColliders.GetUnsafe(e) scale := s.Scales.GetUnsafe(e) position := s.Positions.GetUnsafe(e) @@ -76,7 +76,7 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { }, float32(rotation.Degrees()), rl.DarkGreen, e) return true }) - s.CircleColliders.EachEntity()(func(e ecs.Entity) bool { + s.CircleColliders.EachEntity(func(e ecs.Entity) bool { col := s.CircleColliders.GetUnsafe(e) scale := s.Scales.GetUnsafe(e) pos := s.Positions.GetUnsafe(e) @@ -103,7 +103,7 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { // TODO: Parallelize this with future batches feature // Follow child to texture of parent box collider - s.BoxColliders.EachEntity()(func(e ecs.Entity) bool { + s.BoxColliders.EachEntity(func(e ecs.Entity) bool { parentAABB := s.AABBs.GetUnsafe(e) parentPosition := s.Positions.GetUnsafe(e) col := s.BoxColliders.GetUnsafe(e) @@ -145,7 +145,7 @@ func (s *DebugInfoSystem) Run(dt time.Duration) bool { }) // TODO: Parallelize this with future batches feature // Follow child to texture of parent circle collider - s.CircleColliders.EachEntity()(func(e ecs.Entity) bool { + s.CircleColliders.EachEntity(func(e ecs.Entity) bool { parentAABB := s.AABBs.GetUnsafe(e) pos := s.Positions.GetUnsafe(e) col := s.CircleColliders.GetUnsafe(e) diff --git a/examples/new-api/systems/hp.go b/examples/new-api/systems/hp.go index a6fe547e..b6907ccb 100644 --- a/examples/new-api/systems/hp.go +++ b/examples/new-api/systems/hp.go @@ -35,13 +35,13 @@ type HpSystem struct { func (s *HpSystem) Init() {} func (s *HpSystem) Run(dt time.Duration) { - s.Hps.EachEntity()(func(e ecs.Entity) bool { + s.Hps.EachEntity(func(e ecs.Entity) bool { hp := s.Hps.GetUnsafe(e) if hp.Hp <= 0 { asteroid := s.Asteroids.GetUnsafe(e) player := s.Players.GetUnsafe(e) - s.AsteroidSceneManager.EachComponent()(func(a *components.AsteroidSceneManager) bool { + s.AsteroidSceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { if asteroid != nil { a.PlayerScore += hp.MaxHp } diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 9c8875a7..984c5688 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -75,7 +75,7 @@ func (s *PlayerSystem) Run() { var speed float32 = 300 - for e := range s.Controllers.EachEntity() { + s.Controllers.EachEntity(func(e ecs.Entity) bool { velocity := s.Velocities.GetUnsafe(e) flip := s.Flips.GetUnsafe(e) animationState := s.AnimationStates.GetUnsafe(e) @@ -110,7 +110,7 @@ func (s *PlayerSystem) Run() { if rl.IsKeyPressed(rl.KeyK) { s.EntityManager.Delete(e) } - } - + return true + }) } func (s *PlayerSystem) Destroy() {} diff --git a/examples/new-api/systems/render-bogdan.go b/examples/new-api/systems/render-bogdan.go index 591cf93b..38cd1389 100644 --- a/examples/new-api/systems/render-bogdan.go +++ b/examples/new-api/systems/render-bogdan.go @@ -81,14 +81,14 @@ func (s *RenderBogdanSystem) Run(dt time.Duration) bool { rl.DrawLine(0, i*gridSize, 1024, i*gridSize, rl.Green) } s.render() - s.ColliderBoxes.EachEntity()(func(e ecs.Entity) bool { + s.ColliderBoxes.EachEntity(func(e ecs.Entity) bool { box := s.ColliderBoxes.GetUnsafe(e) pos := s.Positions.GetUnsafe(e) rl.DrawRectangleLines(int32(pos.XY.X), int32(pos.XY.Y), int32(box.WH.X), int32(box.WH.Y), rl.Red) return true }) - s.Collisions.EachEntity()(func(entity ecs.Entity) bool { + s.Collisions.EachEntity(func(entity ecs.Entity) bool { pos := s.Positions.GetUnsafe(entity) rl.DrawRectangle(int32(pos.XY.X), int32(pos.XY.X), 16, 16, rl.Red) return true @@ -108,7 +108,7 @@ func (s *RenderBogdanSystem) render() { if cap(s.renderList) < s.Renderables.Len() { s.renderList = append(s.renderList, make([]renderEntry, 0, s.Renderables.Len()-cap(s.renderList))...) } - s.Renderables.EachEntity()(func(e ecs.Entity) bool { + s.Renderables.EachEntity(func(e ecs.Entity) bool { sprite := s.SpriteMatrixes.Get(e) renderOrder := s.RenderOrders.GetUnsafe(e) s.renderList = append(s.renderList, renderEntry{ @@ -162,7 +162,7 @@ func (s *RenderBogdanSystem) prepareRender(dt time.Duration) { func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) animation := s.AnimationPlayers.GetUnsafe(entity) if animation == nil { @@ -180,7 +180,7 @@ func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) mirrored := s.Flips.GetUnsafe(entity) if mirrored == nil { @@ -199,7 +199,7 @@ func (s *RenderBogdanSystem) prepareFlips(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() //dts := dt.Seconds() - s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) position := s.Positions.GetUnsafe(entity) if position == nil { @@ -217,7 +217,7 @@ func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Durati func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) rotation := s.Rotations.GetUnsafe(entity) if rotation == nil { @@ -230,7 +230,7 @@ func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.GetUnsafe(entity) scale := s.Scales.GetUnsafe(entity) if scale == nil { @@ -244,7 +244,7 @@ func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { func (s *RenderBogdanSystem) prepareTints(wg *sync.WaitGroup) { defer wg.Done() - s.RlTexturePros.EachEntity()(func(entity ecs.Entity) bool { + s.RlTexturePros.EachEntity(func(entity ecs.Entity) bool { tr := s.RlTexturePros.GetUnsafe(entity) tint := s.Tints.GetUnsafe(entity) if tint == nil { diff --git a/examples/new-api/systems/render-overlay.go b/examples/new-api/systems/render-overlay.go index a7049979..25e3ac1a 100644 --- a/examples/new-api/systems/render-overlay.go +++ b/examples/new-api/systems/render-overlay.go @@ -154,7 +154,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } } - s.Cameras.EachEntity()(func(entity ecs.Entity) bool { + s.Cameras.EachEntity(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) fb := s.FrameBuffer2D.GetUnsafe(entity) switch fb.Layer { @@ -168,7 +168,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { rl.BeginMode2D(camera.Camera2D) cameraRect := camera.Rect() - s.CollisionCells.EachEntity()(func(e ecs.Entity) bool { + s.CollisionCells.EachEntity(func(e ecs.Entity) bool { cell := s.CollisionCells.GetUnsafe(e) assert.NotNil(cell) @@ -200,7 +200,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } return true }) - s.CollisionChunks.EachEntity()(func(e ecs.Entity) bool { + s.CollisionChunks.EachEntity(func(e ecs.Entity) bool { chunk := s.CollisionChunks.GetUnsafe(e) assert.NotNil(chunk) @@ -217,7 +217,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { tree := s.BvhTrees.GetUnsafe(e) assert.NotNil(tree) - tree.AabbNodes.EachData()(func(a *stdcomponents.AABB) bool { + tree.AabbNodes.EachData(func(a *stdcomponents.AABB) bool { // Simple AABB culling if s.intersects(cameraRect, a.Rect()) { rl.DrawRectangleRec(rl.Rectangle{ @@ -248,7 +248,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } return true }) - s.AABBs.EachEntity()(func(e ecs.Entity) bool { + s.AABBs.EachEntity(func(e ecs.Entity) bool { aabb := s.AABBs.GetUnsafe(e) clr := rl.Green isSleeping := s.ColliderSleepStateComponentManager.GetUnsafe(e) @@ -265,7 +265,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } return true }) - s.Collisions.EachEntity()(func(entity ecs.Entity) bool { + s.Collisions.EachEntity(func(entity ecs.Entity) bool { pos := s.Positions.GetUnsafe(entity) rec := vectors.Rectangle{ X: pos.XY.X - 8, @@ -278,7 +278,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { } return true }) - s.Textures.EachComponent()(func(r *stdcomponents.RLTexturePro) bool { + s.Textures.EachComponent(func(r *stdcomponents.RLTexturePro) bool { rec := vectors.Rectangle{ X: r.Dest.X - 2, Y: r.Dest.Y - 2, @@ -304,7 +304,7 @@ func (s *RenderOverlaySystem) Run(dt time.Duration) bool { rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), x, y+fontSize*6, fontSize, rl.RayWhite) rl.DrawText(fmt.Sprintf("%d debugLvl", s.debugLvl), x, y+fontSize*7, 20, rl.RayWhite) // Game over - s.SceneManager.EachComponent()(func(a *components.AsteroidSceneManager) bool { + s.SceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), x, y+fontSize*4, 20, rl.RayWhite) rl.DrawText(fmt.Sprintf("Score: %d", a.PlayerScore), x, y+fontSize*5, 20, rl.RayWhite) if a.PlayerHp <= 0 { diff --git a/examples/new-api/systems/space-spawner.go b/examples/new-api/systems/space-spawner.go index 9d63916d..36163858 100644 --- a/examples/new-api/systems/space-spawner.go +++ b/examples/new-api/systems/space-spawner.go @@ -47,7 +47,7 @@ type SpaceSpawnerSystem struct { func (s *SpaceSpawnerSystem) Init() {} func (s *SpaceSpawnerSystem) Run(dt time.Duration) { - s.SpaceSpawners.EachEntity()(func(e ecs.Entity) bool { + s.SpaceSpawners.EachEntity(func(e ecs.Entity) bool { position := s.Positions.GetUnsafe(e) velocity := s.Velocities.GetUnsafe(e) diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index 0ededb21..ee95d448 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -62,7 +62,7 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - s.SpaceshipIntents.EachEntity()(func(entity ecs.Entity) bool { + s.SpaceshipIntents.EachEntity(func(entity ecs.Entity) bool { intent := s.SpaceshipIntents.GetUnsafe(entity) vel := s.Velocities.GetUnsafe(entity) rot := s.Rotations.GetUnsafe(entity) diff --git a/examples/new-api/systems/spatial-audio.go b/examples/new-api/systems/spatial-audio.go index 469a7651..b03baa15 100644 --- a/examples/new-api/systems/spatial-audio.go +++ b/examples/new-api/systems/spatial-audio.go @@ -58,7 +58,7 @@ func (s *SpatialAudioSystem) Run(dt time.Duration) { var mainCamera ecs.Entity // TODO: Add listener component? Then we need position component on it... - s.Cameras.EachEntity()(func(entity ecs.Entity) bool { + s.Cameras.EachEntity(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) assert.NotNil(camera) if camera.Layer == config.MainCameraLayer { diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 28ad937d..1e7399c1 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -221,16 +221,16 @@ func (c *SharedComponentManager[T]) Clean() { // Iterators // ======================================================== -func (c *SharedComponentManager[T]) EachComponent() func(yield func(*T) bool) { +func (c *SharedComponentManager[T]) EachComponent(yield func(*T) bool) { c.assertBegin() defer c.assertEnd() - return c.components.EachData() + c.components.EachData(yield) } -func (c *SharedComponentManager[T]) EachEntity() func(yield func(Entity) bool) { +func (c *SharedComponentManager[T]) EachEntity(yield func(Entity) bool) { c.assertBegin() defer c.assertEnd() - return c.entities.EachDataValue() + c.entities.EachDataValue(yield) } func (c *SharedComponentManager[T]) Each() func(yield func(Entity, *T) bool) { diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 57c0ae43..09785e38 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -244,16 +244,16 @@ func (c *ComponentManager[T]) Clean() { // Iterators // ======================================================== -func (c *ComponentManager[T]) EachComponent() func(yield func(entity *T) bool) { +func (c *ComponentManager[T]) EachComponent(yield func(entity *T) bool) { c.assertBegin() defer c.assertEnd() - return c.components.EachData() + c.components.EachData(yield) } -func (c *ComponentManager[T]) EachEntity() func(yield func(entity Entity) bool) { +func (c *ComponentManager[T]) EachEntity(yield func(entity Entity) bool) { c.assertBegin() defer c.assertEnd() - return c.entities.EachDataValue() + c.entities.EachDataValue(yield) } func (c *ComponentManager[T]) Each() func(yield func(entity Entity, component *T) bool) { @@ -369,7 +369,7 @@ func (c *ComponentManager[T]) getChangesBinary(source *PagedArray[Entity]) Compo components := make([]T, 0, changesLen) entities := make([]Entity, 0, changesLen) - source.EachData()(func(e *Entity) bool { + source.EachData(func(e *Entity) bool { assert.True(e != nil) entId := *e assert.True(c.Has(entId)) diff --git a/pkg/ecs/component-table_bench_test.go b/pkg/ecs/component-table_bench_test.go index 3eff8527..2265ad71 100644 --- a/pkg/ecs/component-table_bench_test.go +++ b/pkg/ecs/component-table_bench_test.go @@ -7,7 +7,20 @@ const maxComponentsLen = 1024 func BenchmarkComponentBitTable_SetAndTest(b *testing.B) { // using a fixed maximum components length + // create and extend the table table := NewComponentBitTable(maxComponentsLen) + for i := Entity(0); i < testEntitiesLen; i++ { + comp := ComponentId(i % maxComponentsLen) + table.Create(i) + table.Set(i, comp) + if !table.Test(i, comp) { + b.Fatalf("BitTable: expected entity %d to have component %d set", i, comp) + } + } + for i := Entity(0); i < testEntitiesLen; i++ { + table.Delete(i) + } + b.ReportAllocs() for b.Loop() { for i := Entity(0); i < testEntitiesLen; i++ { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 59a5765f..dbc6fd0e 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -115,7 +115,6 @@ func (a *PagedArray[T]) extend() { } func (a *PagedArray[T]) Append(value T) *T { - var result *T if a.currentPageIndex >= len(a.book) { a.extend() } @@ -130,7 +129,7 @@ func (a *PagedArray[T]) Append(value T) *T { page = a.book[a.currentPageIndex] } page.data[page.len] = value - result = &page.data[page.len] + result := &page.data[page.len] page.len++ a.len++ return result @@ -298,43 +297,39 @@ func (a *PagedArray[T]) EachParallel(numWorkers int) func(yield func(int, *T, in } } -func (a *PagedArray[T]) EachData() func(yield func(*T) bool) { - return func(yield func(*T) bool) { - var page *ArrayPage[T] - var book = a.book +func (a *PagedArray[T]) EachData(yield func(*T) bool) { + var page *ArrayPage[T] + var book = a.book - if a.len == 0 { - return - } + if a.len == 0 { + return + } - for i := a.currentPageIndex; i >= 0; i-- { - page = book[i] + for i := a.currentPageIndex; i >= 0; i-- { + page = book[i] - for j := page.len - 1; j >= 0; j-- { - if !yield(&page.data[j]) { - return - } + for j := page.len - 1; j >= 0; j-- { + if !yield(&page.data[j]) { + return } } } } -func (a *PagedArray[T]) EachDataValue() func(yield func(T) bool) { - return func(yield func(T) bool) { - var page *ArrayPage[T] - var book = a.book +func (a *PagedArray[T]) EachDataValue(yield func(T) bool) { + var page *ArrayPage[T] + var book = a.book - if a.len == 0 { - return - } + if a.len == 0 { + return + } - for i := a.currentPageIndex; i >= 0; i-- { - page = book[i] + for i := a.currentPageIndex; i >= 0; i-- { + page = book[i] - for j := page.len - 1; j >= 0; j-- { - if !yield(page.data[j]) { - return - } + for j := page.len - 1; j >= 0; j-- { + if !yield(page.data[j]) { + return } } } diff --git a/stdcomponents/camera.go b/stdcomponents/camera.go index c8efe378..8bcfbe8d 100644 --- a/stdcomponents/camera.go +++ b/stdcomponents/camera.go @@ -49,7 +49,7 @@ type Camera struct { Tint color.RGBA } -func (c Camera) Rect() vectors.Rectangle { +func (c *Camera) Rect() vectors.Rectangle { // Calculate the non-rotated top-left corner of the view rectangle x := c.Target.X - (c.Offset.X / c.Zoom) y := c.Target.Y - (c.Offset.Y / c.Zoom) diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index 634056bc..62bd10c6 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -25,7 +25,7 @@ type AnimationSpriteMatrixSystem struct { func (s *AnimationSpriteMatrixSystem) Init() {} func (s *AnimationSpriteMatrixSystem) Run() { - s.AnimationPlayers.EachEntity()(func(e ecs.Entity) bool { + s.AnimationPlayers.EachEntity(func(e ecs.Entity) bool { animationPlayer := s.AnimationPlayers.GetUnsafe(e) spriteMatrix := s.SpriteMatrixes.Get(e) if spriteMatrix == nil { diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 1ceb07a2..dad9f7d2 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -79,7 +79,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { } // Fill trees - s.GenericCollider.EachEntity()(func(entity ecs.Entity) bool { + s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { aabb := s.AABB.GetUnsafe(entity) layer := s.GenericCollider.GetUnsafe(entity).Layer @@ -126,7 +126,7 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions() { func (s *CollisionDetectionBVHSystem) registerCollisionEvents() { for i := range s.collisionEvents { events := &s.collisionEvents[i] - events.EachData()(func(event *CollisionEvent) bool { + events.EachData(func(event *CollisionEvent) bool { pair := CollisionPair{event.entityA, event.entityB} s.currentCollisions[pair] = struct{}{} displacement := event.normal.Scale(event.depth) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 796f275e..1b8a7040 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -69,7 +69,7 @@ func (s *CollisionDetectionSystem) Init() { } func (s *CollisionDetectionSystem) Run(dt time.Duration) { - s.CollisionGridComponentManager.EachComponent()(func(grid *stdcomponents.CollisionGrid) bool { + s.CollisionGridComponentManager.EachComponent(func(grid *stdcomponents.CollisionGrid) bool { s.gridLookup[grid.Layer] = grid return true }) @@ -188,7 +188,7 @@ func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEnti Scale: scaleA.XY, } - for entityB := range potentialEntities.EachDataValue() { + for entityB := range potentialEntities.EachDataValue { aabbB := s.AABB.GetUnsafe(entityB) assert.NotNil(aabbB) @@ -262,7 +262,7 @@ func (s *CollisionDetectionSystem) narrowPhase(entityA ecs.Entity, potentialEnti func (s *CollisionDetectionSystem) registerCollisionEvents() { for i := range s.collisionEventAcc { events := &s.collisionEventAcc[i] - events.EachData()(func(event *CollisionEvent) bool { + events.EachData(func(event *CollisionEvent) bool { pair := CollisionPair{event.entityA, event.entityB} s.currentCollisions[pair] = struct{}{} displacement := event.normal.Scale(event.depth) diff --git a/stdsystems/collision-reslution.go b/stdsystems/collision-reslution.go index 1698658a..a5f9b3c2 100644 --- a/stdsystems/collision-reslution.go +++ b/stdsystems/collision-reslution.go @@ -17,6 +17,7 @@ package stdsystems import ( "github.com/negrel/assert" "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" "gomp/vectors" "time" @@ -36,7 +37,7 @@ type CollisionResolutionSystem struct { func (s *CollisionResolutionSystem) Init() {} func (s *CollisionResolutionSystem) Run(dt time.Duration) { - s.Collisions.EachComponent()(func(collision *stdcomponents.Collision) bool { + s.Collisions.ProcessComponents(func(collision *stdcomponents.Collision, _ worker.WorkerId) { if collision.State == stdcomponents.CollisionStateEnter || collision.State == stdcomponents.CollisionStateStay { // Resolve penetration var displacement vectors.Vec2 @@ -46,7 +47,7 @@ func (s *CollisionResolutionSystem) Run(dt time.Duration) { rigidbody2 := s.RigidBodies.GetUnsafe(collision.E2) if rigidbody1 == nil || rigidbody2 == nil { - return true + return } if !rigidbody1.IsStatic && !rigidbody2.IsStatic { @@ -79,7 +80,7 @@ func (s *CollisionResolutionSystem) Run(dt time.Duration) { velocityAlongNormal := relativeVelocity.Dot(collision.Normal) if velocityAlongNormal > 0 { - return true + return } e := float32(1.0) // Coefficient of restitution (elasticity) @@ -96,7 +97,6 @@ func (s *CollisionResolutionSystem) Run(dt time.Duration) { velocity2.SetVec2(velocity2.Vec2().Add(impulse.Scale(1 / rigidbody2.Mass))) } } - return true }) } func (s *CollisionResolutionSystem) Destroy() {} diff --git a/stdsystems/collision-setup.go b/stdsystems/collision-setup.go index 403552cc..ed3b9dd9 100644 --- a/stdsystems/collision-setup.go +++ b/stdsystems/collision-setup.go @@ -83,7 +83,7 @@ func (s *CollisionSetupSystem) Destroy() { } func (s *CollisionSetupSystem) setup() { // Prepare grids - s.CollisionGridComponentManager.EachEntity()(func(entity ecs.Entity) bool { + s.CollisionGridComponentManager.EachEntity(func(entity ecs.Entity) bool { grid := s.CollisionGridComponentManager.GetUnsafe(entity) assert.NotNil(grid) @@ -308,7 +308,7 @@ func (s *CollisionSetupSystem) setup() { }) for i := range s.clearCellAccumulator { v := &s.clearCellAccumulator[i] - v.EachDataValue()(func(cellEntity ecs.Entity) bool { + v.EachDataValue(func(cellEntity ecs.Entity) bool { cell := s.CollisionCellComponentManager.GetUnsafe(cellEntity) assert.NotNil(cell) @@ -318,7 +318,7 @@ func (s *CollisionSetupSystem) setup() { grid.CellMap.Delete(cell.Index) return true }) - v.EachDataValue()(func(cellEntity ecs.Entity) bool { + v.EachDataValue(func(cellEntity ecs.Entity) bool { s.EntityManager.Delete(cellEntity) return true }) diff --git a/stdsystems/culling.go b/stdsystems/culling.go index 7db4aa7b..98993ffc 100644 --- a/stdsystems/culling.go +++ b/stdsystems/culling.go @@ -52,7 +52,7 @@ func (s *CullingSystem) Run(dt time.Duration) { r.Observed = false }) - s.Cameras.EachEntity()(func(entity ecs.Entity) bool { + s.Cameras.EachEntity(func(entity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(entity) cameraRect := camera.Rect() diff --git a/stdsystems/render-2d-cameras.go b/stdsystems/render-2d-cameras.go index cce1936c..ce17819f 100644 --- a/stdsystems/render-2d-cameras.go +++ b/stdsystems/render-2d-cameras.go @@ -73,7 +73,7 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { s.renderObjectsSorted = make([]renderObjectSorted, 0, max(s.RenderVisibles.Len(), cap(s.renderObjects)*2)) } - s.RenderVisibles.EachEntity()(func(entity ecs.Entity) bool { + s.RenderVisibles.EachEntity(func(entity ecs.Entity) bool { o := s.RenderOrders.GetUnsafe(entity) assert.NotNil(o) @@ -110,7 +110,7 @@ func (s *Render2DCamerasSystem) Run(dt time.Duration) { }) } - s.Cameras.EachEntity()(func(cameraEntity ecs.Entity) bool { + s.Cameras.EachEntity(func(cameraEntity ecs.Entity) bool { camera := s.Cameras.GetUnsafe(cameraEntity) assert.NotNil(camera) renderTexture := s.RenderTexture2D.GetUnsafe(cameraEntity) diff --git a/stdsystems/render.go b/stdsystems/render.go index 496acb36..9d11fea0 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -41,7 +41,7 @@ func (s *RenderSystem) Init() { } func (s *RenderSystem) Run(dt time.Duration) bool { - s.FrameBuffer2D.EachComponent()(func(c *stdcomponents.FrameBuffer2D) bool { + s.FrameBuffer2D.EachComponent(func(c *stdcomponents.FrameBuffer2D) bool { s.frames = append(s.frames, *c) return true }) diff --git a/stdsystems/sprite-matrix.go b/stdsystems/sprite-matrix.go index 83fcdf53..537b19fa 100644 --- a/stdsystems/sprite-matrix.go +++ b/stdsystems/sprite-matrix.go @@ -26,7 +26,7 @@ type SpriteMatrixSystem struct { func (s *SpriteMatrixSystem) Init() {} func (s *SpriteMatrixSystem) Run() { - s.SpriteMatrixes.EachEntity()(func(entity ecs.Entity) bool { + s.SpriteMatrixes.EachEntity(func(entity ecs.Entity) bool { spriteMatrix := s.SpriteMatrixes.Get(entity) // position := s.Positions.GetUnsafe(entity) animationState := s.AnimationStates.GetUnsafe(entity) // diff --git a/stdsystems/ysort.go b/stdsystems/ysort.go index bc2c0360..b42f7c5b 100644 --- a/stdsystems/ysort.go +++ b/stdsystems/ysort.go @@ -16,6 +16,7 @@ package stdsystems import ( "gomp/pkg/ecs" + "gomp/pkg/worker" "gomp/stdcomponents" ) @@ -34,7 +35,7 @@ type YSortSystem struct { func (s *YSortSystem) Init() {} func (s *YSortSystem) Run() { - s.YSorts.EachEntity()(func(entity ecs.Entity) bool { + s.YSorts.ProcessEntities(func(entity ecs.Entity, _ worker.WorkerId) { pos := s.Positions.GetUnsafe(entity) renderOrder := s.RenderOrders.GetUnsafe(entity) @@ -44,8 +45,6 @@ func (s *YSortSystem) Run() { // Preserve original Z layer but add Y-based offset //renderOrder.CalculatedZ = float32(int(pos.Z)) + yDepth renderOrder.CalculatedZ = yDepth - - return true }) } func (s *YSortSystem) Destroy() {} From 2b6cdb629df174818613f2bc7fcd67981bdeb38b Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 15 May 2025 14:09:17 +0300 Subject: [PATCH 195/196] add test wasyan --- internal/wasyan-server/main.go | 111 ++++++++++++------------------ internal/wasyan-server/modules.go | 25 +++++++ internal/wasyan-server/node.go | 70 +++++++++++++++++++ internal/wasyan-wasm/bindings.go | 15 +++- internal/wasyan-wasm/main.go | 20 ++++-- internal/wasyan-wasm/wasm_test.go | 62 +++++++++++++++++ internal/wasyan/shared.go | 12 ++++ taskfile.yml | 12 ++++ 8 files changed, 252 insertions(+), 75 deletions(-) create mode 100644 internal/wasyan-server/modules.go create mode 100644 internal/wasyan-server/node.go create mode 100644 internal/wasyan-wasm/wasm_test.go diff --git a/internal/wasyan-server/main.go b/internal/wasyan-server/main.go index c0b2c980..51d82c59 100644 --- a/internal/wasyan-server/main.go +++ b/internal/wasyan-server/main.go @@ -7,7 +7,8 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- HromRU Donated 2 500 RUB +<- Еблан Donated 228 RUB Thank you for your support! */ @@ -16,7 +17,6 @@ package main import ( "context" - "errors" "fmt" "github.com/gofiber/fiber/v2" "github.com/tetratelabs/wazero" @@ -27,73 +27,49 @@ import ( "log" "os" "strconv" - "sync" - "sync/atomic" "time" - "unsafe" ) -type NodeInstance struct { - Module api.Module -} - -type NodeManager struct { - Instances [2]NodeInstance - LastUsedInstanceIndex atomic.Int32 - config wazero.ModuleConfig - runtime wazero.Runtime - mx sync.Mutex -} - -func (nm *NodeManager) LoadWasm(ctx context.Context, file string) error { - wasm, err := os.ReadFile(file) - if err != nil { - return errors.New("Error opening file:" + err.Error()) - } - - compiledWasm, err := nm.runtime.CompileModule(ctx, wasm) - if err != nil { - return errors.New("Error compiling module:" + err.Error()) - } - - instance1, err := nm.runtime.InstantiateModule(ctx, compiledWasm, nm.config) - if err != nil { - return errors.New("Error creating instance1:" + err.Error()) - } - - instance2, err := nm.runtime.InstantiateModule(ctx, compiledWasm, nm.config) - if err != nil { - return errors.New("Error creating instance2:" + err.Error()) - } - - { - nm.mx.Lock() - defer nm.mx.Unlock() - nm.Instances[0].Module = instance1 - nm.Instances[1].Module = instance2 - nm.LastUsedInstanceIndex.Store(0) - } - - return nil -} - func main() { var ctx = context.Background() var nodeManager = NodeManager{} var game = wasyan.Game{ - Position: vectors.Vec2{X: 0, Y: 0}, - Velocity: vectors.Vec2{X: 50, Y: 10}, + Position: vectors.Vec2{X: 1, Y: 2}, + Velocity: vectors.Vec2{X: 3, Y: 4}, } nodeManager.runtime = wazero.NewRuntime(ctx) defer nodeManager.runtime.Close(ctx) + //var sizeOfGame = int(unsafe.Sizeof(game)) + //var getGameModule = Module{ + // Fn: api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) { + // gameBytes := unsafe.Slice((*byte)(unsafe.Pointer(&game)), sizeOfGame) + // gameRef := api.DecodeU32(stack[0]) + // mod.Memory().Write(gameRef, gameBytes) + // }), + // Params: []api.ValueType{api.ValueTypeI32}, + // Results: []api.ValueType{}, + //} + // + //var setGameModule = Module{ + // Fn: api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) { + // gameRef := api.DecodeU32(stack[0]) + // r, _ := mod.Memory().Read(gameRef, uint32(unsafe.Sizeof(game))) + // localGame := (*wasyan.Game)(unsafe.Pointer(unsafe.SliceData(r))) + // game = *localGame + // }), + // Params: []api.ValueType{api.ValueTypeI32}, + // Results: []api.ValueType{}, + //} + _, err := nodeManager.runtime.NewHostModuleBuilder("env"). - NewFunctionBuilder(). - WithFunc(func(ctx context.Context) uint64 { - return api.EncodeExternref(uintptr(unsafe.Pointer(&game))) - }). - Export("get_game"). + //NewFunctionBuilder(). + //WithGoModuleFunction(getGameModule.Fn, getGameModule.Params, getGameModule.Results). + //Export("get_game"). + //NewFunctionBuilder(). + //WithGoModuleFunction(setGameModule.Fn, setGameModule.Params, setGameModule.Results). + //Export("set_game"). Instantiate(ctx) if err != nil { log.Panicln(err) @@ -102,7 +78,9 @@ func main() { wasi_snapshot_preview1.MustInstantiate(ctx, nodeManager.runtime) // Configure the module to initialize the reactor. - nodeManager.config = wazero.NewModuleConfig().WithStartFunctions("_initialize") + nodeManager.config = wazero.NewModuleConfig(). + WithStdout(os.Stdout). + WithStderr(os.Stderr) var app = fiber.New() @@ -118,11 +96,12 @@ func main() { if err != nil { return c.SendString("Error loading module:\n" + err.Error()) } + log.Println("Instance updated") return c.SendString("Instance updated") }) - app.Get("/add", func(c *fiber.Ctx) error { + app.Get("/internal/add", func(c *fiber.Ctx) error { a, err := strconv.Atoi(c.Query("a")) if err != nil { return c.SendString("Error parsing a:" + err.Error()) @@ -133,10 +112,10 @@ func main() { } start := time.Now() - result := wasyan.Add(int32(a), int32(b)) + wasyan.UpdateGame(&game, vectors.Vec2{X: float32(a), Y: float32(b)}) duration := time.Since(start).String() - return c.SendString("Result is " + fmt.Sprint(result) + "\n" + duration) + return c.SendString("Result is " + fmt.Sprint(game) + "\n" + duration) }) app.Get("/call/:name", func(c *fiber.Ctx) error { @@ -150,8 +129,7 @@ func main() { return c.SendString("Error parsing b:" + err.Error()) } - index := nodeManager.LastUsedInstanceIndex.Add(1) % int32(len(nodeManager.Instances)) - instance := &nodeManager.Instances[index] + instance := &nodeManager.Instances module := instance.Module if module == nil { return c.SendString("Module not found") @@ -163,15 +141,14 @@ func main() { } start := time.Now() - results, err := fn.Call(c.Context(), api.EncodeI32(int32(a)), api.EncodeI32(int32(b))) + results, err := fn.Call(c.Context(), api.EncodeU32(uint32(a)), api.EncodeU32(uint32(b))) duration := time.Since(start).String() if err != nil { - return c.SendString("Error calling RPC" + err.Error()) + log.Println(err) + return c.SendString("Error calling RPC\n" + err.Error()) } - result := (*float32)(unsafe.Pointer(api.DecodeExternref(results[0]))) - game.Velocity.X += *result - return c.SendString("Result is " + fmt.Sprint(*result) + "\n" + "Game is " + fmt.Sprint(game) + "\n" + duration) + return c.SendString("Result is " + fmt.Sprint(results) + "\n" + "Game is " + fmt.Sprint(game) + "\n" + duration) }) log.Fatal(app.Listen(":3000")) diff --git a/internal/wasyan-server/modules.go b/internal/wasyan-server/modules.go new file mode 100644 index 00000000..de138299 --- /dev/null +++ b/internal/wasyan-server/modules.go @@ -0,0 +1,25 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "github.com/tetratelabs/wazero/api" +) + +type Module struct { + Fn api.GoModuleFunction + Params []api.ValueType + Results []api.ValueType +} diff --git a/internal/wasyan-server/node.go b/internal/wasyan-server/node.go new file mode 100644 index 00000000..9aa67f49 --- /dev/null +++ b/internal/wasyan-server/node.go @@ -0,0 +1,70 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "context" + "errors" + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" + "os" + "sync" + "sync/atomic" +) + +type NodeInstance struct { + Module api.Module +} + +type NodeManager struct { + Instances NodeInstance + LastUsedInstanceIndex atomic.Int32 + config wazero.ModuleConfig + runtime wazero.Runtime + mx sync.Mutex +} + +func (nm *NodeManager) LoadWasm(ctx context.Context, file string) error { + wasm, err := os.ReadFile(file) + if err != nil { + return errors.New("Error opening file:" + err.Error()) + } + + compiledWasm, err := nm.runtime.CompileModule(ctx, wasm) + if err != nil { + return errors.New("Error compiling module:" + err.Error()) + } + + if nm.Instances.Module != nil { + err := nm.Instances.Module.Close(ctx) + if err != nil { + return errors.New("Error closing module:" + err.Error()) + } + } + + mod, err := nm.runtime.InstantiateModule(ctx, compiledWasm, nm.config) + if err != nil { + return errors.New("Error creating instance:" + err.Error()) + } + + { + nm.mx.Lock() + defer nm.mx.Unlock() + nm.Instances.Module = mod + nm.LastUsedInstanceIndex.Store(0) + } + + return nil +} diff --git a/internal/wasyan-wasm/bindings.go b/internal/wasyan-wasm/bindings.go index b1d763ce..416ff6a4 100644 --- a/internal/wasyan-wasm/bindings.go +++ b/internal/wasyan-wasm/bindings.go @@ -1,3 +1,5 @@ +//go:build wasm + /* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed @@ -20,8 +22,15 @@ import ( ) //go:wasmimport env get_game -func get_game() uint64 +func get_game(gameRef uint32) + +//go:wasmimport env set_game +func set_game(gameRef uint32) + +func GetGame(game *wasyan.Game) { + get_game(uint32(uintptr(unsafe.Pointer(game)))) +} -func GetGame() *wasyan.Game { - return (*wasyan.Game)(unsafe.Pointer(uintptr(get_game()))) +func SetGame(game *wasyan.Game) { + set_game(uint32(uintptr(unsafe.Pointer(game)))) } diff --git a/internal/wasyan-wasm/main.go b/internal/wasyan-wasm/main.go index fb5c2a1d..5f2272c0 100644 --- a/internal/wasyan-wasm/main.go +++ b/internal/wasyan-wasm/main.go @@ -1,3 +1,5 @@ +//go:build wasm + /* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed @@ -14,14 +16,22 @@ Thank you for your support! package main -import "unsafe" +import ( + "fmt" + "runtime" +) func main() { - println("main") + fmt.Println(runtime.GOARCH, runtime.GOOS) } //go:wasmexport add -func add(a, b int32) uint64 { - game := GetGame() - return uint64(uintptr(unsafe.Pointer(&game.Velocity.X))) +func add(a, b uint32) { + //panic("lol") + //var game wasyan.Game + //GetGame(&game) + //wasyan.UpdateGame(&game, vectors.Vec2{X: float32(a), Y: float32(b)}) + + //SetGame(&game) + var _ = a + b } diff --git a/internal/wasyan-wasm/wasm_test.go b/internal/wasyan-wasm/wasm_test.go new file mode 100644 index 00000000..5d029df1 --- /dev/null +++ b/internal/wasyan-wasm/wasm_test.go @@ -0,0 +1,62 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "context" + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" + "os" + "testing" +) + +func TestWASM(t *testing.T) { + ctx := context.Background() + r := wazero.NewRuntime(ctx) + defer r.Close(ctx) + + // Instantiate WASI + wasi_snapshot_preview1.MustInstantiate(ctx, r) + + config := wazero.NewModuleConfig(). + WithStartFunctions("_start"). // Or "_initialize" based on the module type + //WithStartFunctions("_initialize"). // Or "_initialize" based on the module type + WithStdout(os.Stdout). + WithStderr(os.Stderr) + + // Load WASM module + wasm, err := os.ReadFile("hello.wasm") + if err != nil { + t.Fatal(err) + } + + compiledWasm, err := r.CompileModule(ctx, wasm) + if err != nil { + t.Fatal(err) + } + + mod, err := r.InstantiateModule(ctx, compiledWasm, config) + if err != nil { + t.Fatal(err) + } + + // Call the function + add := mod.ExportedFunction("add") + _, err = add.Call(ctx, api.EncodeU32(1), api.EncodeU32(2)) + if err != nil { + t.Fatal(err) + } +} diff --git a/internal/wasyan/shared.go b/internal/wasyan/shared.go index 6e423fb6..5a5192ca 100644 --- a/internal/wasyan/shared.go +++ b/internal/wasyan/shared.go @@ -16,6 +16,7 @@ package wasyan import ( "gomp/vectors" + "math" "sync" ) @@ -35,6 +36,17 @@ func Add(a, b int32) int32 { return int32(c) } +func UpdateGame(game *Game, newVelocity vectors.Vec2) { + game.Velocity = newVelocity + for i := 0; i < 1_000_000; i++ { + j := float32(i) + game.Velocity.X = float32(math.Sin(float64(j + game.Velocity.Y))) + game.Velocity.Y = float32(math.Cos(float64(j + game.Velocity.X))) + } + game.Position.X += game.Velocity.X + game.Position.Y += game.Velocity.Y +} + type Game struct { Position vectors.Vec2 Velocity vectors.Vec2 diff --git a/taskfile.yml b/taskfile.yml index c0821549..3ed4aa38 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -112,3 +112,15 @@ tasks: cmds: - go build -buildmode=c-shared -o ./hello.wasm ./internal/wasyan-wasm - curl http://127.0.0.1:3000/update/hello.wasm + + + build-tiny-wasm: + env: + GOOS: wasip1 + GOARCH: wasm + watch: true + sources: + - 'internal/wasyan-wasm/*.go' + cmds: + - tinygo build -o ./hello.wasm -target=wasi ./internal/wasyan-wasm + - curl http://127.0.0.1:3000/update/hello.wasm From c5b565082ab08881fa9504634aaa8f4bfb34ffc4 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 15 May 2025 17:55:34 +0300 Subject: [PATCH 196/196] update wasyan --- go.mod | 29 +++------- go.sum | 99 +------------------------------- internal/wasyan-runner/main.go | 86 +++++++++++++++++++++++++++ internal/wasyan-server/main.go | 56 +++++++++--------- internal/wasyan-wasm/bindings.go | 2 - internal/wasyan-wasm/main.go | 22 +++---- taskfile.yml | 2 +- 7 files changed, 137 insertions(+), 159 deletions(-) create mode 100644 internal/wasyan-runner/main.go diff --git a/go.mod b/go.mod index 49e644d3..a9180438 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,16 @@ go 1.24 replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go v1.1.7 require ( + github.com/Zyko0/go-sdl3 v0.0.0-20250324113244-771f317184f7 + github.com/bytecodealliance/wasmtime-go/v32 v32.0.0 github.com/coder/websocket v1.8.12 + github.com/fasthttp/websocket v1.5.8 + github.com/felixge/fgprof v0.9.5 github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5 + github.com/gofiber/contrib/websocket v1.3.4 + github.com/gofiber/fiber/v2 v2.52.6 github.com/hajimehoshi/ebiten/v2 v2.8.6 + github.com/hajimehoshi/go-steamworks v0.0.0-20241112125913-96b2a6baef69 github.com/jakecoffman/cp/v2 v2.1.0 github.com/jfreymuth/go-sdl3 v0.1.3-0.20250226211328-622f8250e21c github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345 @@ -18,55 +25,37 @@ require ( github.com/quic-go/quic-go v0.49.0 github.com/sevenNt/echo-pprof v0.1.1-0.20230131020615-4dd36891e14b github.com/stretchr/testify v1.10.0 + github.com/tetratelabs/wazero v1.9.0 github.com/veandco/go-sdl2 v0.4.40 github.com/yohamta/donburi v1.15.7 golang.org/x/time v0.10.0 ) require ( - github.com/Zyko0/go-sdl3 v0.0.0-20250324113244-771f317184f7 // indirect github.com/Zyko0/purego-gen v0.0.0-20250308152853-097c3ba1e28a // indirect github.com/andybalholm/brotli v1.1.1 // indirect - github.com/bytecodealliance/wasmtime-go v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fasthttp/websocket v1.5.8 // indirect - github.com/felixge/fgprof v0.9.5 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/gofiber/contrib/websocket v1.3.4 // indirect - github.com/gofiber/fiber/v2 v2.52.6 // indirect - github.com/gofiber/fiber/v3 v3.0.0-beta.4 // indirect - github.com/gofiber/schema v1.3.0 // indirect - github.com/gofiber/utils/v2 v2.0.0-beta.8 // indirect github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect - github.com/hajimehoshi/go-steamworks v0.0.0-20241112125913-96b2a6baef69 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect - github.com/paulmach/orb v0.11.1 // indirect - github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect - github.com/tetratelabs/wazero v1.9.0 // indirect - github.com/tinylib/msgp v1.2.5 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.62.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect - github.com/x448/float16 v0.8.4 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/tools v0.25.0 // indirect - gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -83,6 +72,6 @@ require ( golang.org/x/net v0.40.0 // indirect golang.org/x/sync v0.14.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.25.0 google.golang.org/protobuf v1.36.1 ) diff --git a/go.sum b/go.sum index d3e3e6b3..05830514 100644 --- a/go.sum +++ b/go.sum @@ -18,16 +18,13 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/bytecodealliance/wasmtime-go v1.0.0 h1:9u9gqaUiaJeN5IoD1L7egD8atOnTGyJcNp8BhkL9cUU= -github.com/bytecodealliance/wasmtime-go v1.0.0/go.mod h1:jjlqQbWUfVSbehpErw3UoWFndBXRRMvfikYH6KsCwOg= +github.com/bytecodealliance/wasmtime-go/v32 v32.0.0 h1:rGZFaIExj4h5EwehU+rnALJSq+OOvDEcutiyH+qSAzo= +github.com/bytecodealliance/wasmtime-go/v32 v32.0.0/go.mod h1:JRtCAOIPwpAESq6DD3L11RFyTDYQnQf4UgsbOwbKvpU= github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= @@ -41,8 +38,6 @@ github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0 github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b h1:/KAOJuXR4cWaQIiA9xBMDSQJ1JXq5gZHdSK8prrtUqQ= github.com/ebitengine/purego v0.9.0-alpha.2.0.20250124174847-29f0104e3c2b/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fasthttp/websocket v1.5.8 h1:k5DpirKkftIF/w1R8ZzjSgARJrs54Je9YJK37DL/Ah8= @@ -53,8 +48,6 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= -github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5 h1:k8ZAxLgb/p5TvCi5VHFHM8JdnjwShNK4A0bLIwbktAU= github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -71,28 +64,17 @@ github.com/gofiber/contrib/websocket v1.3.4 h1:tWeBdbJ8q0WFQXariLN4dBIbGH9KBU75s github.com/gofiber/contrib/websocket v1.3.4/go.mod h1:kTFBPC6YENCnKfKx0BoOFjgXxdz7E85/STdkmZPEmPs= github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= -github.com/gofiber/fiber/v3 v3.0.0-beta.4 h1:KzDSavvhG7m81NIsmnu5l3ZDbVS4feCidl4xlIfu6V0= -github.com/gofiber/fiber/v3 v3.0.0-beta.4/go.mod h1:/WFUoHRkZEsGHyy2+fYcdqi109IVOFbVwxv1n1RU+kk= -github.com/gofiber/schema v1.3.0 h1:K3F3wYzAY+aivfCCEHPufCthu5/13r/lzp1nuk6mr3Q= -github.com/gofiber/schema v1.3.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c= -github.com/gofiber/utils/v2 v2.0.0-beta.8 h1:ZifwbHZqZO3YJsx1ZhDsWnPjaQ7C0YD20LHt+DQeXOU= -github.com/gofiber/utils/v2 v2.0.0-beta.8/go.mod h1:1lCBo9vEF4RFEtTgWntipnaScJZQiM8rrsYycLZ4n9c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= @@ -101,8 +83,6 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -124,7 +104,6 @@ github.com/hajimehoshi/ebiten/v2 v2.8.6 h1:Dkd/sYI0TYyZRCE7GVxV59XC+WCi2BbGAbIBj github.com/hajimehoshi/ebiten/v2 v2.8.6/go.mod h1:cCQ3np7rdmaJa1ZnvslraVlpxNb3wCjEnAP1LHNyXNA= github.com/hajimehoshi/go-steamworks v0.0.0-20241112125913-96b2a6baef69 h1:R5DmT3Ffuccaf3U7DiLYpro2avyBz7D112v9eqm8NvE= github.com/hajimehoshi/go-steamworks v0.0.0-20241112125913-96b2a6baef69/go.mod h1:xQbwn4VSK2CwjfAgpolFH8MYSu96NQZhiOuksu1vvdY= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/jakecoffman/cp/v2 v2.1.0 h1:s0almZ7zDZs9JY35ciUgCoVKTMmdPkokF1dxHg226Wo= github.com/jakecoffman/cp/v2 v2.1.0/go.mod h1:Q0hFU7Kk6PMw4dwgFtvBC6O4KTm7ewiLuHrXtHMicyU= @@ -138,9 +117,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345 h1:t3LCgVzMRS2q7fL4T9pgshIUQuqBQD+ERMKUJNgL+Qo= github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345/go.mod h1:bUv1BcdO0uvGAb0mT5PDSInh67TlbzXBBL4/vVwcEEs= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -158,11 +135,8 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1 github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= @@ -171,7 +145,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/negrel/assert v0.5.0 h1:woWYcJDBNLMxpIv9XaRacA0l9K6cStkoYygu58J4DzI= @@ -184,13 +157,7 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= -github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= -github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= -github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= -github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= -github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -203,7 +170,6 @@ github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 h1:mvIS9aGirkzuYmH github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7/go.mod h1:EbI+KMbALSVE2s0YFOQpR4uj66zBh9ter5P4CBMSuvA= github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -248,33 +214,20 @@ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= -github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.62.0 h1:8dKRBX/y2rCzyc6903Zu1+3qN0H/d2MsxPPmVNamiH0= github.com/valyala/fasthttp v1.62.0/go.mod h1:FCINgr4GKdKqV8Q0xv8b+UxPV+H/O5nNFo3D+r54Htg= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/U= github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yohamta/donburi v1.15.7 h1:so/vHf1L133d0SFVrCUzMMueh2ko39wRkrcpNLdzvz8= github.com/yohamta/donburi v1.15.7/go.mod h1:FdjU9hpwAsAs1qRvqsSTJimPJ0dipvdnr9hMJXYc1Rk= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= @@ -283,11 +236,6 @@ golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -298,8 +246,6 @@ golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -310,13 +256,6 @@ golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -328,12 +267,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -341,27 +274,12 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -369,19 +287,11 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -398,12 +308,9 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= diff --git a/internal/wasyan-runner/main.go b/internal/wasyan-runner/main.go new file mode 100644 index 00000000..d71e7b88 --- /dev/null +++ b/internal/wasyan-runner/main.go @@ -0,0 +1,86 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "fmt" + "github.com/bytecodealliance/wasmtime-go/v32" + "log" + "os" +) + +func main() { + engine := wasmtime.NewEngine() + defer engine.Close() + store := wasmtime.NewStore(engine) + defer store.Close() + + // Create a linker with WASI functions defined within it + linker := wasmtime.NewLinker(engine) + err := linker.DefineWasi() + check(err) + + wasiConfig := wasmtime.NewWasiConfig() + defer wasiConfig.Close() + + wasiConfig.InheritStderr() + wasiConfig.InheritStdout() + + store.SetWasi(wasiConfig) + + wasm, err := os.ReadFile("hello.wasm") + check(err) + + module, err := wasmtime.NewModule(engine, wasm) + check(err) + defer module.Close() + + instance, err := linker.Instantiate(store, module) + check(err) + + //get_game := wasmtime.WrapFunc(store, func(gameRef uint32) { + // + //}) + // + //set_game := wasmtime.WrapFunc(store, func(gameRef uint32) { + // + //}) + //instance, err := wasmtime.NewInstance(store, module, []wasmtime.AsExtern{get_game, set_game}) + + // Init + initFn := instance.GetFunc(store, "_start") + if initFn == nil { + initFn = instance.GetFunc(store, "_initialize") + if initFn == nil { + panic("Init function not found") + } + } + + _, err = initFn.Call(store) + check(err) + log.Println("Initialized") + + //call + gcd := instance.GetFunc(store, "add") + val, err := gcd.Call(store, 6, 27) + check(err) + fmt.Printf("add(6, 27) = %d\n", val.(int32)) +} + +func check(err error) { + if err != nil { + panic(err) + } +} diff --git a/internal/wasyan-server/main.go b/internal/wasyan-server/main.go index 51d82c59..6673b677 100644 --- a/internal/wasyan-server/main.go +++ b/internal/wasyan-server/main.go @@ -28,6 +28,7 @@ import ( "os" "strconv" "time" + "unsafe" ) func main() { @@ -41,35 +42,35 @@ func main() { nodeManager.runtime = wazero.NewRuntime(ctx) defer nodeManager.runtime.Close(ctx) - //var sizeOfGame = int(unsafe.Sizeof(game)) - //var getGameModule = Module{ - // Fn: api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) { - // gameBytes := unsafe.Slice((*byte)(unsafe.Pointer(&game)), sizeOfGame) - // gameRef := api.DecodeU32(stack[0]) - // mod.Memory().Write(gameRef, gameBytes) - // }), - // Params: []api.ValueType{api.ValueTypeI32}, - // Results: []api.ValueType{}, - //} - // - //var setGameModule = Module{ - // Fn: api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) { - // gameRef := api.DecodeU32(stack[0]) - // r, _ := mod.Memory().Read(gameRef, uint32(unsafe.Sizeof(game))) - // localGame := (*wasyan.Game)(unsafe.Pointer(unsafe.SliceData(r))) - // game = *localGame - // }), - // Params: []api.ValueType{api.ValueTypeI32}, - // Results: []api.ValueType{}, - //} + var sizeOfGame = int(unsafe.Sizeof(game)) + var getGameModule = Module{ + Fn: api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) { + gameBytes := unsafe.Slice((*byte)(unsafe.Pointer(&game)), sizeOfGame) + gameRef := api.DecodeU32(stack[0]) + mod.Memory().Write(gameRef, gameBytes) + }), + Params: []api.ValueType{api.ValueTypeI32}, + Results: []api.ValueType{}, + } + + var setGameModule = Module{ + Fn: api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) { + gameRef := api.DecodeU32(stack[0]) + r, _ := mod.Memory().Read(gameRef, uint32(unsafe.Sizeof(game))) + localGame := (*wasyan.Game)(unsafe.Pointer(unsafe.SliceData(r))) + game = *localGame + }), + Params: []api.ValueType{api.ValueTypeI32}, + Results: []api.ValueType{}, + } _, err := nodeManager.runtime.NewHostModuleBuilder("env"). - //NewFunctionBuilder(). - //WithGoModuleFunction(getGameModule.Fn, getGameModule.Params, getGameModule.Results). - //Export("get_game"). - //NewFunctionBuilder(). - //WithGoModuleFunction(setGameModule.Fn, setGameModule.Params, setGameModule.Results). - //Export("set_game"). + NewFunctionBuilder(). + WithGoModuleFunction(getGameModule.Fn, getGameModule.Params, getGameModule.Results). + Export("get_game"). + NewFunctionBuilder(). + WithGoModuleFunction(setGameModule.Fn, setGameModule.Params, setGameModule.Results). + Export("set_game"). Instantiate(ctx) if err != nil { log.Panicln(err) @@ -79,6 +80,7 @@ func main() { // Configure the module to initialize the reactor. nodeManager.config = wazero.NewModuleConfig(). + WithStartFunctions("_start", "_initialize"). WithStdout(os.Stdout). WithStderr(os.Stderr) diff --git a/internal/wasyan-wasm/bindings.go b/internal/wasyan-wasm/bindings.go index 416ff6a4..2abf1ec2 100644 --- a/internal/wasyan-wasm/bindings.go +++ b/internal/wasyan-wasm/bindings.go @@ -1,5 +1,3 @@ -//go:build wasm - /* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed diff --git a/internal/wasyan-wasm/main.go b/internal/wasyan-wasm/main.go index 5f2272c0..7c5131e3 100644 --- a/internal/wasyan-wasm/main.go +++ b/internal/wasyan-wasm/main.go @@ -1,5 +1,3 @@ -//go:build wasm - /* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed @@ -17,21 +15,19 @@ Thank you for your support! package main import ( - "fmt" - "runtime" + "gomp/internal/wasyan" + "gomp/vectors" ) -func main() { - fmt.Println(runtime.GOARCH, runtime.GOOS) -} +func main() {} //go:wasmexport add -func add(a, b uint32) { +func add(a, b uint32) uint32 { //panic("lol") - //var game wasyan.Game - //GetGame(&game) - //wasyan.UpdateGame(&game, vectors.Vec2{X: float32(a), Y: float32(b)}) + var game wasyan.Game + GetGame(&game) + wasyan.UpdateGame(&game, vectors.Vec2{X: float32(a), Y: float32(b)}) + SetGame(&game) - //SetGame(&game) - var _ = a + b + return a + b } diff --git a/taskfile.yml b/taskfile.yml index 3ed4aa38..ffe20f96 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -122,5 +122,5 @@ tasks: sources: - 'internal/wasyan-wasm/*.go' cmds: - - tinygo build -o ./hello.wasm -target=wasi ./internal/wasyan-wasm + - tinygo build -buildmode=c-shared -o ./hello.wasm -target=wasi ./internal/wasyan-wasm - curl http://127.0.0.1:3000/update/hello.wasm

    K&1AzB+uHEFaUKEWjwVKUZ3>+P{y@RQ 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

  • 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�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 z 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 20 & IndexMask), int(key >> 10 & IndexMask), int(key & IndexMask) +} + +const ( + k = 1024*1024*1024*4 - 1 + i0 = k >> 30 & IndexMask + i1 = k >> 20 & IndexMask + i2 = k >> 10 & IndexMask + i3 = k & IndexMask +) diff --git a/pkg/ecs/lookup_test.go b/pkg/ecs/lookup_test.go new file mode 100644 index 00000000..9316bdca --- /dev/null +++ b/pkg/ecs/lookup_test.go @@ -0,0 +1,78 @@ +package ecs + +import ( + "sync" + "testing" +) + +func TestLookupMap(t *testing.T) { + lm := &LookupMap[int]{} + + // Test setting and getting a value + key := uint32(0x12345678) + value := 42 + lm.Set(key, value) + + // Test getting the value + ret, ok := lm.Get(key) + if !ok { + t.Errorf("Expected to find value for key %x, but got not found", key) + } + if ret != value { + t.Errorf("Expected value %d for key %x, but got %d", value, key, ret) + } + + // Test getting a non-existent key + nonExistentKey := uint32(0x87654321) + ret, ok = lm.Get(nonExistentKey) + if ok { + t.Errorf("Expected not to find value for key %x, but got found", nonExistentKey) + } + if ret != 0 { + t.Errorf("Expected zero value for non-existent key %x, but got %d", nonExistentKey, ret) + } + + // Test overwriting a value + newValue := 100 + lm.Set(key, newValue) + ret, ok = lm.Get(key) + if !ok { + t.Errorf("Expected to find value for key %x after overwrite, but got not found", key) + } + if ret != newValue { + t.Errorf("Expected value %d for key %x after overwrite, but got %d", newValue, key, ret) + } +} + +func TestLookupMapConcurrency(t *testing.T) { + lm := &LookupMap[int]{} + key := uint32(0x12345678) + value := 42 + + // Test concurrent writes + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + lm.Set(key, value+i) + }(i) + } + wg.Wait() + + // Test concurrent reads + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + ret, ok := lm.Get(key) + if !ok { + t.Errorf("Expected to find value for key %x, but got not found", key) + } + if ret < value || ret >= value+100 { + t.Errorf("Expected value between %d and %d for key %x, but got %d", value, value+99, key, ret) + } + }() + } + wg.Wait() +} diff --git a/taskfile.yml b/taskfile.yml index 068ec4d3..04c46c35 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -47,6 +47,8 @@ tasks: CGO_ENABLED: 1 cmds: - go build -o ./.dist/game-win64.exe examples/new-api/game.go + - go build -o ./.dist/cgo-game.exe internal/sdl3/sdl-game.go + - CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -tags static -ldflags "-s -w" internal/sdl2/sdl-game.go build-mac: - task: build-darwin-amd64 From 0d4cc5dc2989e3e5e39b159335305b98ac45e080 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Feb 2025 11:24:55 +0300 Subject: [PATCH 025/196] feat internal sdl3 cgo test --- go.mod | 1 + go.sum | 2 + internal/sdl2/sdl-game.go | 18 +------- internal/sdl3-cgo/sdl3cgo-game.go | 55 ++++++++++++++++++++++++ internal/{sdl3 => sdl3-pure}/sdl-game.go | 4 +- taskfile.yml | 2 +- 6 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 internal/sdl3-cgo/sdl3cgo-game.go rename internal/{sdl3 => sdl3-pure}/sdl-game.go (93%) diff --git a/go.mod b/go.mod index 6d301af9..d9e10343 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect + github.com/jfreymuth/go-sdl3 v0.1.3-0.20250226211328-622f8250e21c // indirect github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect diff --git a/go.sum b/go.sum index de5a54ab..0193f7a3 100644 --- a/go.sum +++ b/go.sum @@ -86,6 +86,8 @@ github.com/jakecoffman/cp/v2 v2.1.0/go.mod h1:Q0hFU7Kk6PMw4dwgFtvBC6O4KTm7ewiLuH github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +github.com/jfreymuth/go-sdl3 v0.1.3-0.20250226211328-622f8250e21c h1:c7cKhwG6vsLRR2HHpAdhVX9KAD+wlB3WfF2cnZ3q4Es= +github.com/jfreymuth/go-sdl3 v0.1.3-0.20250226211328-622f8250e21c/go.mod h1:37mPz4b0UCp9QTMrMiKfhq2mDofqBW7ccklWSKH9YL0= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345 h1:t3LCgVzMRS2q7fL4T9pgshIUQuqBQD+ERMKUJNgL+Qo= diff --git a/internal/sdl2/sdl-game.go b/internal/sdl2/sdl-game.go index 6aabcd6f..8c0cb157 100644 --- a/internal/sdl2/sdl-game.go +++ b/internal/sdl2/sdl-game.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "github.com/veandco/go-sdl2/ttf" "log" "os" "time" @@ -21,8 +20,7 @@ var winWidth, winHeight int32 = 800, 600 func run() int { var window *sdl.Window var renderer *sdl.Renderer - var font *ttf.Font - var text *sdl.Surface + var rects []sdl.FRect = make([]sdl.FRect, 100_000) window, err := sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, @@ -44,20 +42,6 @@ func run() int { rects[i] = sdl.FRect{X: float32(i%780) + 10, Y: float32(i / 780), W: 1, H: 1} } - // Load the font for our text - if font, err = ttf.OpenFont(fontPath, fontSize); err != nil { - return 0 - } - defer font.Close() - - // Create a red text with the font - if text, err = font.RenderUTF8Blended("Hello, World!", sdl.Color{R: 255, G: 0, B: 0, A: 255}); err != nil { - return 0 - } - defer text.Free() - - // Draw the text around the center of the window - var dt time.Duration = time.Second var fps int64 updatedAt := time.Now() diff --git a/internal/sdl3-cgo/sdl3cgo-game.go b/internal/sdl3-cgo/sdl3cgo-game.go new file mode 100644 index 00000000..70636c15 --- /dev/null +++ b/internal/sdl3-cgo/sdl3cgo-game.go @@ -0,0 +1,55 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import "C" +import "github.com/jfreymuth/go-sdl3/sdl" + +func main() { + must(sdl.Init(sdl.InitVideo)) + defer sdl.Quit() + + w, r, e := sdl.CreateWindowAndRenderer("sld c-go", 640, 480, sdl.WindowResizable) + must(e) + defer w.Destroy() + defer r.Destroy() + +Outer: + for { + var event sdl.Event + sdl.PollEvent(&event) + switch event.Type() { + case sdl.EventQuit: + break Outer + case sdl.EventKeyDown: + if event.Keyboard().Scancode == sdl.ScancodeEscape { + break Outer + } + } + + must(r.SetDrawColor(0, 0, 0, 255)) + must(r.Clear()) + must(r.SetDrawColor(255, 255, 255, 255)) + must(r.DebugText(10, 10, "Hello")) + must(r.Present()) + } +} + +func must(err error) { + if err != nil { + println(err.Error()) + panic(err) + } +} diff --git a/internal/sdl3/sdl-game.go b/internal/sdl3-pure/sdl-game.go similarity index 93% rename from internal/sdl3/sdl-game.go rename to internal/sdl3-pure/sdl-game.go index 995751cf..a94b662e 100644 --- a/internal/sdl3/sdl-game.go +++ b/internal/sdl3-pure/sdl-game.go @@ -25,8 +25,8 @@ func main() { fmt.Println("CPU Profile Started") defer fmt.Println("CPU Profile Stopped") - //if !sdl3.SetHint(sdl3.HintFramebufferAcceleration, "1") { - // panic(sdl3.GetError()) + //if !sdl3-pure.SetHint(sdl3-pure.HintFramebufferAcceleration, "1") { + // panic(sdl3-pure.GetError()) //} defer sdl.Quit() diff --git a/taskfile.yml b/taskfile.yml index 04c46c35..6153d1b6 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -47,7 +47,7 @@ tasks: CGO_ENABLED: 1 cmds: - go build -o ./.dist/game-win64.exe examples/new-api/game.go - - go build -o ./.dist/cgo-game.exe internal/sdl3/sdl-game.go + - go build -o ./.dist/cgo-game.exe internal/sdl3-pure/sdl-game.go - CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -tags static -ldflags "-s -w" internal/sdl2/sdl-game.go build-mac: From c8bacb0841c35e963d28e97616d47c9c8a0bec92 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Feb 2025 17:35:25 +0300 Subject: [PATCH 026/196] refactor render -> rl-render.go --- desktop-components.go | 4 +- examples/new-api/entities/player.go | 5 + examples/new-api/instances/component-list.go | 6 +- examples/new-api/instances/system-list.go | 16 +- examples/new-api/scenes/main-scene.go | 46 +--- examples/new-api/systems/player.go | 5 +- go.mod | 10 +- go.sum | 2 - pkg/ecs/component-manager.go | 22 ++ pkg/ecs/paged-array.go | 36 +++ pkg/ecs/paged-map.go | 4 +- stdcomponents/ids.go | 4 +- stdcomponents/renderable.go | 30 +++ .../{texture-render.go => rl-texture-pro.go} | 8 +- stdsystems/animation-player.go | 2 +- stdsystems/animation-spritematrix.go | 3 +- stdsystems/render.go | 84 ------- stdsystems/rl-render.go | 212 ++++++++++++++++++ stdsystems/sprite-matrix.go | 52 +++++ stdsystems/sprite-sheet.go | 55 +++++ stdsystems/sprite.go | 86 +++++++ stdsystems/texture-render-animation.go | 47 ---- stdsystems/texture-render-flip.go | 47 ---- stdsystems/texture-render-position.go | 51 ----- stdsystems/texture-render-rotation.go | 42 ---- stdsystems/texture-render-scale.go | 42 ---- stdsystems/texture-render-sprite.go | 88 -------- stdsystems/texture-render-spritematrix.go | 74 ------ stdsystems/texture-render-spritesheet.go | 57 ----- stdsystems/texture-render-tint.go | 45 ---- 30 files changed, 530 insertions(+), 655 deletions(-) create mode 100644 stdcomponents/renderable.go rename stdcomponents/{texture-render.go => rl-texture-pro.go} (71%) delete mode 100644 stdsystems/render.go create mode 100644 stdsystems/rl-render.go create mode 100644 stdsystems/sprite-matrix.go create mode 100644 stdsystems/sprite-sheet.go create mode 100644 stdsystems/sprite.go delete mode 100644 stdsystems/texture-render-animation.go delete mode 100644 stdsystems/texture-render-flip.go delete mode 100644 stdsystems/texture-render-position.go delete mode 100644 stdsystems/texture-render-rotation.go delete mode 100644 stdsystems/texture-render-scale.go delete mode 100644 stdsystems/texture-render-sprite.go delete mode 100644 stdsystems/texture-render-spritematrix.go delete mode 100644 stdsystems/texture-render-spritesheet.go delete mode 100644 stdsystems/texture-render-tint.go diff --git a/desktop-components.go b/desktop-components.go index b726cfd8..d1b1ba62 100644 --- a/desktop-components.go +++ b/desktop-components.go @@ -31,7 +31,7 @@ func NewDesktopComponents() DesktopComponents { Tint: stdcomponents.NewTintComponentManager(), AnimationPlayer: stdcomponents.NewAnimationPlayerComponentManager(), AnimationState: stdcomponents.NewAnimationStateComponentManager(), - TextureRender: stdcomponents.NewTextureRenderComponentManager(), + RlTexturePro: stdcomponents.NewRlTextureProComponentManager(), Network: stdcomponents.NewNetworkComponentManager(), } } @@ -48,6 +48,6 @@ type DesktopComponents struct { Tint stdcomponents.TintComponentManager AnimationPlayer stdcomponents.AnimationPlayerComponentManager AnimationState stdcomponents.AnimationStateComponentManager - TextureRender stdcomponents.TextureRenderComponentManager + RlTexturePro stdcomponents.RLTextureProComponentManager Network stdcomponents.NetworkComponentManager } diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index 88505f99..2cc120d1 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -34,6 +34,7 @@ type Player struct { AnimationPlayer *stdcomponents.AnimationPlayer AnimationState *stdcomponents.AnimationState Flip *stdcomponents.Flip + Renderable *stdcomponents.Renderable } var playerSpriteMatrix = stdcomponents.SpriteMatrix{ @@ -76,6 +77,7 @@ func CreatePlayer( animationStates *stdcomponents.AnimationStateComponentManager, tints *stdcomponents.TintComponentManager, flips *stdcomponents.FlipComponentManager, + renderables *stdcomponents.RenderableComponentManager, ) (player Player) { // Creating new player @@ -118,5 +120,8 @@ func CreatePlayer( // Adding Flip component player.Flip = flips.Create(entity, stdcomponents.Flip{}) + // Adding renderable component + player.Renderable = renderables.Create(entity, stdcomponents.SpriteMatrixRenderableType) + return player } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index b0c734c9..653e7e7a 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -31,8 +31,9 @@ type ComponentList struct { Tint stdcomponents.TintComponentManager AnimationPlayer stdcomponents.AnimationPlayerComponentManager AnimationState stdcomponents.AnimationStateComponentManager - TextureRender stdcomponents.TextureRenderComponentManager + RLTexturePro stdcomponents.RLTextureProComponentManager Network stdcomponents.NetworkComponentManager + Renderable stdcomponents.RenderableComponentManager Health components.HealthComponentManager Controller components.ControllerComponentManager @@ -51,8 +52,9 @@ func NewComponentList() ComponentList { Tint: stdcomponents.NewTintComponentManager(), AnimationPlayer: stdcomponents.NewAnimationPlayerComponentManager(), AnimationState: stdcomponents.NewAnimationStateComponentManager(), - TextureRender: stdcomponents.NewTextureRenderComponentManager(), + RLTexturePro: stdcomponents.NewRlTextureProComponentManager(), Network: stdcomponents.NewNetworkComponentManager(), + Renderable: stdcomponents.NewRenderableComponentManager(), Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index cf1b473f..44bfac8b 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -33,13 +33,7 @@ func NewSystemList() SystemList { AnimationPlayer: stdsystems.NewAnimationPlayerSystem(), TextureRenderSprite: stdsystems.NewTextureRenderSpriteSystem(), TextureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(), - TextureRenderMatrix: stdsystems.NewTextureRenderMatrixSystem(), - TextureRenderAnimation: stdsystems.NewTextureRenderAnimationSystem(), - TextureRenderFlip: stdsystems.NewTextureRenderFlipSystem(), - TextureRenderPosition: stdsystems.NewTextureRenderPositionSystem(), - TextureRenderRotation: stdsystems.NewTextureRenderRotationSystem(), - TextureRenderScale: stdsystems.NewTextureRenderScaleSystem(), - TextureRenderTint: stdsystems.NewTextureRenderTintSystem(), + SpriteMatrix: stdsystems.NewSpriteMatrixSystem(), AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{assets.Textures}), Render: stdsystems.NewRenderSystem(), } @@ -58,13 +52,7 @@ type SystemList struct { AnimationPlayer stdsystems.AnimationPlayerSystem TextureRenderSprite stdsystems.TextureRenderSpriteSystem TextureRenderSpriteSheet stdsystems.TextureRenderSpriteSheetSystem - TextureRenderMatrix stdsystems.TextureRenderMatrixSystem - TextureRenderAnimation stdsystems.TextureRenderAnimationSystem - TextureRenderFlip stdsystems.TextureRenderFlipSystem - TextureRenderPosition stdsystems.TextureRenderPositionSystem - TextureRenderRotation stdsystems.TextureRenderRotationSystem - TextureRenderScale stdsystems.TextureRenderScaleSystem - TextureRenderTint stdsystems.TextureRenderTintSystem + SpriteMatrix stdsystems.SpriteMatrixSystem AssetLib stdsystems.AssetLibSystem Render stdsystems.RenderSystem } diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index ab282c8a..d3937636 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -45,7 +45,6 @@ func (s *MainScene) Init() { // Scenes s.World.Systems.Player.Init() - s.World.Systems.Velocity.Init() // Network patches @@ -55,18 +54,7 @@ func (s *MainScene) Init() { s.World.Systems.AnimationSpriteMatrix.Init() s.World.Systems.AnimationPlayer.Init() - // Prerender init - s.World.Systems.TextureRenderSprite.Init() - s.World.Systems.TextureRenderSpriteSheet.Init() - s.World.Systems.TextureRenderMatrix.Init() - - // Prerender fill - s.World.Systems.TextureRenderAnimation.Init() - s.World.Systems.TextureRenderFlip.Init() - s.World.Systems.TextureRenderPosition.Init() - s.World.Systems.TextureRenderRotation.Init() - s.World.Systems.TextureRenderScale.Init() - s.World.Systems.TextureRenderTint.Init() + s.World.Systems.SpriteMatrix.Init() // Render s.World.Systems.Render.Init() @@ -95,24 +83,11 @@ func (s *MainScene) Render(dt time.Duration) { s.World.Systems.AnimationSpriteMatrix.Run() s.World.Systems.AnimationPlayer.Run() - // Prerender init - s.World.Systems.TextureRenderSprite.Run() - s.World.Systems.TextureRenderSpriteSheet.Run() - s.World.Systems.TextureRenderMatrix.Run() - - // Prerender fill - s.World.Systems.TextureRenderAnimation.Run() - s.World.Systems.TextureRenderFlip.Run() - s.World.Systems.TextureRenderPosition.Run(dt) - s.World.Systems.TextureRenderRotation.Run() - s.World.Systems.TextureRenderScale.Run() - s.World.Systems.TextureRenderTint.Run() - - // Render + s.World.Systems.SpriteMatrix.Run() s.World.Systems.Debug.Run() - s.World.Systems.AssetLib.Run() - shouldContinue := s.World.Systems.Render.Run() + + shouldContinue := s.World.Systems.Render.Run(dt) if !shouldContinue { s.Game.SetShouldDestroy(true) return @@ -134,18 +109,7 @@ func (s *MainScene) Destroy() { s.World.Systems.AnimationSpriteMatrix.Destroy() s.World.Systems.AnimationPlayer.Destroy() - // Prerender init - s.World.Systems.TextureRenderSprite.Destroy() - s.World.Systems.TextureRenderSpriteSheet.Destroy() - s.World.Systems.TextureRenderMatrix.Destroy() - - // Prerender fill - s.World.Systems.TextureRenderAnimation.Destroy() - s.World.Systems.TextureRenderFlip.Destroy() - s.World.Systems.TextureRenderPosition.Destroy() - s.World.Systems.TextureRenderRotation.Destroy() - s.World.Systems.TextureRenderScale.Destroy() - s.World.Systems.TextureRenderTint.Destroy() + s.World.Systems.SpriteMatrix.Destroy() // Render s.World.Systems.Debug.Destroy() diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 7204ba4d..f4ce22c4 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -33,13 +33,14 @@ type PlayerSystem struct { Flips *stdcomponents.FlipComponentManager HP *components.HealthComponentManager Controllers *components.ControllerComponentManager + Renderables *stdcomponents.RenderableComponentManager } func (s *PlayerSystem) Init() { - for range 100_000 { + for range 20_000 { s.Player = entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, - s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, + s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, ) s.Player.Position.X = 100 + rand.Float32()*700 diff --git a/go.mod b/go.mod index d9e10343..a5351387 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module gomp -go 1.24.0 +go 1.24 replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go v1.1.7 @@ -9,6 +9,8 @@ require ( github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5 github.com/hajimehoshi/ebiten/v2 v2.8.6 github.com/jakecoffman/cp/v2 v2.1.0 + github.com/jfreymuth/go-sdl3 v0.1.3-0.20250226211328-622f8250e21c + github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345 github.com/labstack/echo-contrib v0.17.2 github.com/labstack/gommon v0.4.2 github.com/negrel/assert v0.5.0 @@ -16,6 +18,7 @@ require ( github.com/quic-go/quic-go v0.49.0 github.com/sevenNt/echo-pprof v0.1.1-0.20230131020615-4dd36891e14b github.com/stretchr/testify v1.10.0 + github.com/veandco/go-sdl2 v0.4.40 github.com/yohamta/donburi v1.15.7 golang.org/x/time v0.10.0 ) @@ -27,8 +30,6 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect - github.com/jfreymuth/go-sdl3 v0.1.3-0.20250226211328-622f8250e21c // indirect - github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect @@ -36,7 +37,6 @@ require ( github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/veandco/go-sdl2 v0.4.40 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect golang.org/x/mod v0.21.0 // indirect @@ -58,6 +58,6 @@ require ( golang.org/x/net v0.33.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 + golang.org/x/text v0.21.0 // indirect google.golang.org/protobuf v1.36.1 ) diff --git a/go.sum b/go.sum index 0193f7a3..b0ea89d2 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,6 @@ github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0 github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= -github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 4a89459d..39572832 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -301,6 +301,17 @@ func (c *ComponentManager[T]) EachComponent(yield func(*T) bool) { assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") } +func (c *ComponentManager[T]) EachComponentParallel(yield func(*T) bool) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + + c.components.AllDataParallel(yield) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} + func (c *ComponentManager[T]) EachEntity(yield func(Entity) bool) { assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") @@ -312,6 +323,17 @@ func (c *ComponentManager[T]) EachEntity(yield func(Entity) bool) { assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") } +func (c *ComponentManager[T]) EachEntityParallel(yield func(Entity) bool) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + + c.entities.AllDataValueParallel(yield) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} + func (c *ComponentManager[T]) All(yield func(Entity, *T) bool) { assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 86975f70..aaa2fe84 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -247,6 +247,42 @@ func (a *PagedArray[T]) AllDataValue(yield func(T) bool) { } } +func (a *PagedArray[T]) AllDataValueParallel(yield func(T) bool) { + var page *ArrayPage[T] + var data *[page_size]T + + book := a.book + wg := new(sync.WaitGroup) + gorutineBudget := a.parallelCount + runner := func(data *[page_size]T, startIndex int, wg *sync.WaitGroup) { + defer wg.Done() + for j := startIndex; j >= 0; j-- { + if !yield((data[j])) { + return + } + } + } + + if a.len == 0 { + return + } + + wg.Add(int(a.currentPageIndex) + 1) + for i := a.currentPageIndex; i >= 0; i-- { + page = &book[i] + data = &page.data + + if gorutineBudget > 0 { + go runner(data, page.len-1, wg) + gorutineBudget-- + continue + } + + runner(data, page.len-1, wg) + } + wg.Wait() +} + func (a *PagedArray[T]) AllDataParallel(yield func(*T) bool) { var page *ArrayPage[T] var data *[page_size]T diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 42e294f8..bd8b7949 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -11,9 +11,9 @@ import ( ) const ( - page_size_shift int = 14 + page_size_shift int = 12 page_size int = 1 << page_size_shift - book_size int = 1 << 4 + book_size int = 1 << 2 ) type MapPage[K Entity, V any] map[K]V diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 448ae074..98b7c615 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -15,16 +15,16 @@ const ( PositionComponentId RotationComponentId ScaleComponentId - ViewPositionId FlipComponentId VelocityComponentId SpriteComponentId SpriteSheetComponentId SpriteMatrixComponentId - TextureRenderComponentId + RLTextureProComponentId AnimationPlayerComponentId AnimationStateComponentId TintComponentId NetworkComponentId + RenderableComponentId StdComponentIds ) diff --git a/stdcomponents/renderable.go b/stdcomponents/renderable.go new file mode 100644 index 00000000..92fada06 --- /dev/null +++ b/stdcomponents/renderable.go @@ -0,0 +1,30 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +const ( + InvalidRenderableType Renderable = iota + SpriteMatrixRenderableType +) + +type Renderable uint8 + +type RenderableComponentManager = ecs.ComponentManager[Renderable] + +func NewRenderableComponentManager() RenderableComponentManager { + return ecs.NewComponentManager[Renderable](RenderableComponentId) +} diff --git a/stdcomponents/texture-render.go b/stdcomponents/rl-texture-pro.go similarity index 71% rename from stdcomponents/texture-render.go rename to stdcomponents/rl-texture-pro.go index a35f13de..26f4dc39 100644 --- a/stdcomponents/texture-render.go +++ b/stdcomponents/rl-texture-pro.go @@ -20,7 +20,7 @@ import ( "image/color" ) -type TextureRender struct { +type RLTexturePro struct { Texture *rl.Texture2D Frame rl.Rectangle Origin rl.Vector2 @@ -29,8 +29,8 @@ type TextureRender struct { Rotation float32 } -type TextureRenderComponentManager = ecs.ComponentManager[TextureRender] +type RLTextureProComponentManager = ecs.ComponentManager[RLTexturePro] -func NewTextureRenderComponentManager() TextureRenderComponentManager { - return ecs.NewComponentManager[TextureRender](TextureRenderComponentId) +func NewRlTextureProComponentManager() RLTextureProComponentManager { + return ecs.NewComponentManager[RLTexturePro](RLTextureProComponentId) } diff --git a/stdsystems/animation-player.go b/stdsystems/animation-player.go index c8391806..9e418163 100644 --- a/stdsystems/animation-player.go +++ b/stdsystems/animation-player.go @@ -28,7 +28,7 @@ func (s *AnimationPlayerSystem) Init() { } func (s *AnimationPlayerSystem) Run() { dt := time.Since(s.lastRunAt) - s.AnimationPlayers.AllDataParallel(func(animation *stdcomponents.AnimationPlayer) bool { + s.AnimationPlayers.EachComponentParallel(func(animation *stdcomponents.AnimationPlayer) bool { animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond assert.True(animation.FrameDuration > 0, "frame duration must be greater than 0") diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index 9ff36375..c4a976ea 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -25,7 +25,8 @@ type AnimationSpriteMatrixSystem struct { func (s *AnimationSpriteMatrixSystem) Init() {} func (s *AnimationSpriteMatrixSystem) Run() { - s.AnimationPlayers.AllParallel(func(e ecs.Entity, animationPlayer *stdcomponents.AnimationPlayer) bool { + s.AnimationPlayers.EachEntityParallel(func(e ecs.Entity) bool { + animationPlayer := s.AnimationPlayers.Get(e) spriteMatrix := s.SpriteMatrixes.Get(e) if spriteMatrix == nil { return true diff --git a/stdsystems/render.go b/stdsystems/render.go deleted file mode 100644 index 9c9e64c6..00000000 --- a/stdsystems/render.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - "fmt" - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -const ( - batchSize = 1 << 13 // Maximum batch size supported by Raylib -) - -func NewRenderSystem() RenderSystem { - return RenderSystem{} -} - -type RenderSystem struct { - EntityManager *ecs.EntityManager - TextureRenders *stdcomponents.TextureRenderComponentManager - Positions *stdcomponents.PositionComponentManager - camera rl.Camera2D - trBatch []stdcomponents.TextureRender -} - -func (s *RenderSystem) Init() { - rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") - //InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") - - s.camera = rl.Camera2D{ - Target: rl.NewVector2(0, 0), - Offset: rl.NewVector2(0, 0), - Rotation: 0, - Zoom: 1, - } -} - -func (s *RenderSystem) Run() bool { - if rl.WindowShouldClose() { - return false - } - - textureLen := s.TextureRenders.Len() - if len(s.trBatch) < textureLen { - s.trBatch = make([]stdcomponents.TextureRender, textureLen) - } - s.TextureRenders.RawComponents(s.trBatch) - - rl.BeginDrawing() - rl.ClearBackground(rl.Black) - - for batch := 0; batch < textureLen; batch += batchSize { - endBatch := batch + batchSize - if endBatch > textureLen { - endBatch = textureLen - } - - rl.BeginMode2D(s.camera) - for i := batch; i < endBatch; i++ { - tr := &s.trBatch[i] - txt := *tr.Texture - rl.DrawTexturePro(txt, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) - } - rl.EndMode2D() - } - - rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) - rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) - - rl.EndDrawing() - - return true -} - -func (s *RenderSystem) Destroy() { - rl.CloseWindow() -} diff --git a/stdsystems/rl-render.go b/stdsystems/rl-render.go new file mode 100644 index 00000000..57546575 --- /dev/null +++ b/stdsystems/rl-render.go @@ -0,0 +1,212 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "fmt" + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "math" + "sync" + "time" +) + +const ( + batchSize = 1 << 13 // Maximum batch size supported by Raylib +) + +func NewRenderSystem() RenderSystem { + return RenderSystem{} +} + +type RenderSystem struct { + EntityManager *ecs.EntityManager + RlTexturePros *stdcomponents.RLTextureProComponentManager + Positions *stdcomponents.PositionComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Tints *stdcomponents.TintComponentManager + Flips *stdcomponents.FlipComponentManager + Renderables *stdcomponents.RenderableComponentManager + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + camera rl.Camera2D +} + +func (s *RenderSystem) Init() { + rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") + //InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") + + s.camera = rl.Camera2D{ + Target: rl.NewVector2(0, 0), + Offset: rl.NewVector2(0, 0), + Rotation: 0, + Zoom: 1, + } +} + +func (s *RenderSystem) Run(dt time.Duration) bool { + if rl.WindowShouldClose() { + return false + } + + s.prepareRender(dt) + + rl.BeginDrawing() + rl.ClearBackground(rl.Black) + + rl.BeginMode2D(s.camera) + s.renderWorld() + rl.EndMode2D() + + rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) + rl.DrawFPS(10, 10) + rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) + + rl.EndDrawing() + + return true +} + +func (s *RenderSystem) Destroy() { + rl.CloseWindow() +} + +func (s *RenderSystem) renderWorld() { + s.Renderables.EachEntity(func(entity ecs.Entity) bool { + renderable := s.Renderables.Get(entity) + + switch *renderable { + case stdcomponents.SpriteMatrixRenderableType: + s.renderSpriteMatrix(entity) + default: + panic("unknown renderable type") + } + + return true + }) +} + +func (s *RenderSystem) prepareRender(dt time.Duration) { + wg := new(sync.WaitGroup) + wg.Add(6) + s.prepareAnimations(wg) + go s.prepareFlips(wg) + go s.preparePositions(wg, dt) + go s.prepareRotations(wg) + go s.prepareScales(wg) + go s.prepareTints(wg) + wg.Wait() +} + +func (s *RenderSystem) prepareAnimations(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + animation := s.AnimationPlayers.Get(entity) + if animation == nil { + return true + } + frame := &texturePro.Frame + if animation.Vertical { + frame.Y += frame.Height * float32(animation.Current) + } else { + frame.X += frame.Width * float32(animation.Current) + } + return true + }) +} + +func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + mirrored := s.Flips.Get(entity) + if mirrored == nil { + return true + } + if mirrored.X { + texturePro.Frame.Width *= -1 + } + if mirrored.Y { + texturePro.Frame.Height *= -1 + } + return true + }) +} + +func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { + defer wg.Done() + dts := dt.Seconds() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + position := s.Positions.Get(entity) + if position == nil { + return true + } + decay := 40.0 // DECAY IS TICKRATE DEPENDENT + texturePro.Dest.X = float32(s.expDecay(float64(texturePro.Dest.X), float64(position.X), decay, dts)) + texturePro.Dest.Y = float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.Y), decay, dts)) + + return true + }) +} + +func (s *RenderSystem) prepareRotations(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + rotation := s.Rotations.Get(entity) + if rotation == nil { + return true + } + texturePro.Rotation = rotation.Angle + return true + }) +} + +func (s *RenderSystem) prepareScales(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + scale := s.Scales.Get(entity) + if scale == nil { + return true + } + texturePro.Dest.Width *= scale.X + texturePro.Dest.Height *= scale.Y + return true + }) +} + +func (s *RenderSystem) prepareTints(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + tr := s.RlTexturePros.Get(entity) + tint := s.Tints.Get(entity) + if tint == nil { + return true + } + trTint := &tr.Tint + trTint.A = tint.A + trTint.R = tint.R + trTint.G = tint.G + trTint.B = tint.B + return true + }) +} + +func (s *RenderSystem) renderSpriteMatrix(entity ecs.Entity) { + texturePro := s.RlTexturePros.Get(entity) + rl.DrawTexturePro(*texturePro.Texture, texturePro.Frame, texturePro.Dest, texturePro.Origin, texturePro.Rotation, texturePro.Tint) +} + +func (s *RenderSystem) expDecay(a, b, decay, dt float64) float64 { + return b + (a-b)*(math.Exp(-decay*dt)) +} diff --git a/stdsystems/sprite-matrix.go b/stdsystems/sprite-matrix.go new file mode 100644 index 00000000..056bc59a --- /dev/null +++ b/stdsystems/sprite-matrix.go @@ -0,0 +1,52 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +func NewSpriteMatrixSystem() SpriteMatrixSystem { + return SpriteMatrixSystem{} +} + +// SpriteMatrixSystem is a system that prepares SpriteSheet to be rendered +type SpriteMatrixSystem struct { + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + RLTexturePros *stdcomponents.RLTextureProComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + Positions *stdcomponents.PositionComponentManager +} + +func (s *SpriteMatrixSystem) Init() {} +func (s *SpriteMatrixSystem) Run() { + s.SpriteMatrixes.EachEntityParallel(func(entity ecs.Entity) bool { + spriteMatrix := s.SpriteMatrixes.Get(entity) // + animationState := s.AnimationStates.Get(entity) // + position := s.Positions.Get(entity) + + frame := spriteMatrix.Animations[*animationState].Frame + + tr := s.RLTexturePros.Get(entity) + if tr == nil { + s.RLTexturePros.Create(entity, stdcomponents.RLTexturePro{ + Texture: spriteMatrix.Texture, // + Frame: frame, // + Origin: spriteMatrix.Origin, + Dest: rl.Rectangle{X: position.X, Y: position.Y, Width: frame.Width, Height: frame.Height}, // + }) + } else { + // Run spriteRender + + tr.Frame = frame + } + return true + }) +} +func (s *SpriteMatrixSystem) Destroy() {} diff --git a/stdsystems/sprite-sheet.go b/stdsystems/sprite-sheet.go new file mode 100644 index 00000000..9166f737 --- /dev/null +++ b/stdsystems/sprite-sheet.go @@ -0,0 +1,55 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/stdcomponents" +) + +func NewTextureRenderSpriteSheetSystem() TextureRenderSpriteSheetSystem { + return TextureRenderSpriteSheetSystem{} +} + +// TextureRenderSpriteSheetSystem is a system that prepares SpriteSheet to be rendered +type TextureRenderSpriteSheetSystem struct { + SpriteSheets *stdcomponents.SpriteSheetComponentManager + TextureRenders *stdcomponents.RLTextureProComponentManager +} + +func (s *TextureRenderSpriteSheetSystem) Init() {} +func (s *TextureRenderSpriteSheetSystem) Run() { + //s.SpriteSheets.AllParallel(func(entity ecs.Entity, spriteSheet *stdcomponents.SpriteSheet) bool { + // if spriteSheet == nil { + // return true + // } + // + // tr := s.RlTexturePros.Get(entity) + // if tr == nil { + // // Create new spriteRender + // newRender := stdcomponents.RLTexturePro{ + // Texture: spriteSheet.Texture, + // Frame: spriteSheet.Frame, + // Origin: spriteSheet.Origin, + // Dest: rl.NewRectangle( + // 0, + // 0, + // spriteSheet.Frame.Width, + // spriteSheet.Frame.Height, + // ), + // } + // + // s.RlTexturePros.Create(entity, newRender) + // } else { + // // Run spriteRender + // tr.Texture = spriteSheet.Texture + // tr.Frame = spriteSheet.Frame + // tr.Origin = spriteSheet.Origin + // } + // return true + //}) +} +func (s *TextureRenderSpriteSheetSystem) Destroy() {} diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go new file mode 100644 index 00000000..ccd36067 --- /dev/null +++ b/stdsystems/sprite.go @@ -0,0 +1,86 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +<- Монтажер сука Donated 50 RUB + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/stdcomponents" +) + +func NewTextureRenderSpriteSystem() TextureRenderSpriteSystem { + return TextureRenderSpriteSystem{} +} + +// TextureRenderSpriteSystem is a system that prepares Sprite to be rendered +type TextureRenderSpriteSystem struct { + Sprites *stdcomponents.SpriteComponentManager + TextureRenders *stdcomponents.RLTextureProComponentManager +} + +func (s *TextureRenderSpriteSystem) Init() {} +func (s *TextureRenderSpriteSystem) Run() { + // Run sprites and spriteRenders + //s.Sprites.AllParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { + // if sprite == nil { + // return true + // } + // + // spriteFrame := sprite.Frame + // spriteOrigin := sprite.Origin + // spriteTint := sprite.Tint + // + // tr := s.RlTexturePros.Get(entity) + // if tr == nil { + // // Create new spriteRender + // newRender := stdcomponents.RLTexturePro{ + // Texture: sprite.Texture, + // Frame: sprite.Frame, + // Origin: sprite.Origin, + // Tint: sprite.Tint, + // Dest: rl.NewRectangle( + // 0, + // 0, + // sprite.Frame.Width, + // sprite.Frame.Height, + // ), + // } + // + // s.RlTexturePros.Create(entity, newRender) + // } else { + // // Run spriteRender + // // tr.Texture = sprite.Texture + // trFrame := &tr.Frame + // trFrame.X = spriteFrame.X + // trFrame.Y = spriteFrame.Y + // trFrame.Width = spriteFrame.Width + // trFrame.Height = spriteFrame.Height + // + // trOrigin := &tr.Origin + // trOrigin.X = spriteOrigin.X + // trOrigin.Y = spriteOrigin.Y + // + // trTint := &tr.Tint + // trTint.A = spriteTint.A + // trTint.R = spriteTint.R + // trTint.G = spriteTint.G + // trTint.B = spriteTint.B + // + // trDest := &tr.Dest + // trDest.Width = spriteFrame.Width + // trDest.Height = spriteFrame.Height + // } + // return true + //}) +} +func (s *TextureRenderSpriteSystem) Destroy() {} diff --git a/stdsystems/texture-render-animation.go b/stdsystems/texture-render-animation.go deleted file mode 100644 index 60cd25f7..00000000 --- a/stdsystems/texture-render-animation.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewTextureRenderAnimationSystem() TextureRenderAnimationSystem { - return TextureRenderAnimationSystem{} -} - -// TextureRenderAnimationSystem is a system that sets Position of textureRender -type TextureRenderAnimationSystem struct { - Animations *stdcomponents.AnimationPlayerComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager -} - -func (s *TextureRenderAnimationSystem) Init() {} -func (s *TextureRenderAnimationSystem) Run() { - // Run sprites and spriteRenders - s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { - if tr == nil { - return true - } - - animation := s.Animations.Get(entity) - if animation == nil { - return true - } - - frame := &tr.Frame - if animation.Vertical { - frame.Y += frame.Height * float32(animation.Current) - } else { - frame.X += frame.Width * float32(animation.Current) - } - - return true - }) -} -func (s *TextureRenderAnimationSystem) Destroy() {} diff --git a/stdsystems/texture-render-flip.go b/stdsystems/texture-render-flip.go deleted file mode 100644 index d449db06..00000000 --- a/stdsystems/texture-render-flip.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewTextureRenderFlipSystem() TextureRenderFlipSystem { - return TextureRenderFlipSystem{} -} - -// TextureRenderFlipSystem is a system that sets Scale of textureRender -type TextureRenderFlipSystem struct { - Flips *stdcomponents.FlipComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager -} - -func (s *TextureRenderFlipSystem) Init() {} -func (s *TextureRenderFlipSystem) Run() { - // Run sprites and spriteRenders - s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { - if tr == nil { - return true - } - - mirrored := s.Flips.Get(entity) - if mirrored == nil { - return true - } - - if mirrored.X { - tr.Frame.Width *= -1 - } - if mirrored.Y { - tr.Frame.Height *= -1 - } - - return true - }) -} -func (s *TextureRenderFlipSystem) Destroy() {} diff --git a/stdsystems/texture-render-position.go b/stdsystems/texture-render-position.go deleted file mode 100644 index 6796d6c6..00000000 --- a/stdsystems/texture-render-position.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" - "math" - "time" -) - -func NewTextureRenderPositionSystem() TextureRenderPositionSystem { - return TextureRenderPositionSystem{} -} - -// TextureRenderPositionSystem is a system that sets Position of textureRender -type TextureRenderPositionSystem struct { - Positions *stdcomponents.PositionComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager -} - -func (s *TextureRenderPositionSystem) Init() {} -func (s *TextureRenderPositionSystem) Run(dt time.Duration) { - - dts := dt.Seconds() - s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { - if tr == nil { - return true - } - - position := s.Positions.Get(entity) - if position == nil { - return true - } - - decay := 40.0 // DECAY IS TICKRATE DEPENDENT - tr.Dest.X = float32(expDecay(float64(tr.Dest.X), float64(position.X), decay, dts)) - tr.Dest.Y = float32(expDecay(float64(tr.Dest.Y), float64(position.Y), decay, dts)) - - return true - }) -} -func (s *TextureRenderPositionSystem) Destroy() {} - -func expDecay(a, b, decay, dt float64) float64 { - return b + (a-b)*(math.Exp(-decay*dt)) -} diff --git a/stdsystems/texture-render-rotation.go b/stdsystems/texture-render-rotation.go deleted file mode 100644 index 289b6ddd..00000000 --- a/stdsystems/texture-render-rotation.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewTextureRenderRotationSystem() TextureRenderRotationSystem { - return TextureRenderRotationSystem{} -} - -// TextureRenderRotationSystem is a system that sets Rotation of textureRender -type TextureRenderRotationSystem struct { - Rotations *stdcomponents.RotationComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager -} - -func (s *TextureRenderRotationSystem) Init() {} -func (s *TextureRenderRotationSystem) Run() { - // Run sprites and spriteRenders - s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { - if tr == nil { - return true - } - - rotation := s.Rotations.Get(entity) - if rotation == nil { - return true - } - - tr.Rotation = rotation.Angle - - return true - }) -} -func (s *TextureRenderRotationSystem) Destroy() {} diff --git a/stdsystems/texture-render-scale.go b/stdsystems/texture-render-scale.go deleted file mode 100644 index 5ba21a9d..00000000 --- a/stdsystems/texture-render-scale.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewTextureRenderScaleSystem() TextureRenderScaleSystem { - return TextureRenderScaleSystem{} -} - -// TextureRenderScaleSystem is a system that sets Scale of textureRender -type TextureRenderScaleSystem struct { - Scales *stdcomponents.ScaleComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager -} - -func (s *TextureRenderScaleSystem) Init() {} -func (s *TextureRenderScaleSystem) Run() { - s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { - if tr == nil { - return true - } - - scale := s.Scales.Get(entity) - if scale == nil { - return true - } - - tr.Dest.Width *= scale.X - tr.Dest.Height *= scale.Y - - return true - }) -} -func (s *TextureRenderScaleSystem) Destroy() {} diff --git a/stdsystems/texture-render-sprite.go b/stdsystems/texture-render-sprite.go deleted file mode 100644 index f6bc6f98..00000000 --- a/stdsystems/texture-render-sprite.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -<- Монтажер сука Donated 50 RUB - -Thank you for your support! -*/ - -package stdsystems - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewTextureRenderSpriteSystem() TextureRenderSpriteSystem { - return TextureRenderSpriteSystem{} -} - -// TextureRenderSpriteSystem is a system that prepares Sprite to be rendered -type TextureRenderSpriteSystem struct { - Sprites *stdcomponents.SpriteComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager -} - -func (s *TextureRenderSpriteSystem) Init() {} -func (s *TextureRenderSpriteSystem) Run() { - // Run sprites and spriteRenders - s.Sprites.AllParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { - if sprite == nil { - return true - } - - spriteFrame := sprite.Frame - spriteOrigin := sprite.Origin - spriteTint := sprite.Tint - - tr := s.TextureRenders.Get(entity) - if tr == nil { - // Create new spriteRender - newRender := stdcomponents.TextureRender{ - Texture: sprite.Texture, - Frame: sprite.Frame, - Origin: sprite.Origin, - Tint: sprite.Tint, - Dest: rl.NewRectangle( - 0, - 0, - sprite.Frame.Width, - sprite.Frame.Height, - ), - } - - s.TextureRenders.Create(entity, newRender) - } else { - // Run spriteRender - // tr.Texture = sprite.Texture - trFrame := &tr.Frame - trFrame.X = spriteFrame.X - trFrame.Y = spriteFrame.Y - trFrame.Width = spriteFrame.Width - trFrame.Height = spriteFrame.Height - - trOrigin := &tr.Origin - trOrigin.X = spriteOrigin.X - trOrigin.Y = spriteOrigin.Y - - trTint := &tr.Tint - trTint.A = spriteTint.A - trTint.R = spriteTint.R - trTint.G = spriteTint.G - trTint.B = spriteTint.B - - trDest := &tr.Dest - trDest.Width = spriteFrame.Width - trDest.Height = spriteFrame.Height - } - return true - }) -} -func (s *TextureRenderSpriteSystem) Destroy() {} diff --git a/stdsystems/texture-render-spritematrix.go b/stdsystems/texture-render-spritematrix.go deleted file mode 100644 index 7a4da991..00000000 --- a/stdsystems/texture-render-spritematrix.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewTextureRenderMatrixSystem() TextureRenderMatrixSystem { - return TextureRenderMatrixSystem{} -} - -// TextureRenderMatrixSystem is a system that prepares SpriteSheet to be rendered -type TextureRenderMatrixSystem struct { - SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager - AnimationStates *stdcomponents.AnimationStateComponentManager - Positions *stdcomponents.PositionComponentManager -} - -func (s *TextureRenderMatrixSystem) Init() {} -func (s *TextureRenderMatrixSystem) Run() { - // Run sprites and spriteRenders - s.SpriteMatrixes.AllParallel(func(entity ecs.Entity, spriteMatrix *stdcomponents.SpriteMatrix) bool { - if spriteMatrix == nil { - return true - } - - animationState := s.AnimationStates.Get(entity) - if animationState == nil { - return true - } - - position := s.Positions.Get(entity) - if position == nil { - return true - } - - currentAnimationFrame := spriteMatrix.Animations[*animationState].Frame - - tr := s.TextureRenders.Get(entity) - if tr == nil { - // Create new spriteRender - newRender := stdcomponents.TextureRender{ - Texture: spriteMatrix.Texture, - Origin: spriteMatrix.Origin, - Frame: currentAnimationFrame, - Dest: rl.Rectangle{ - X: position.X, - Y: position.Y, - Width: currentAnimationFrame.Width, - Height: currentAnimationFrame.Height, - }, - } - - s.TextureRenders.Create(entity, newRender) - } else { - // Run spriteRender - tr.Texture = spriteMatrix.Texture - tr.Origin = spriteMatrix.Origin - tr.Dest.Width = currentAnimationFrame.Width - tr.Dest.Height = currentAnimationFrame.Height - tr.Frame = currentAnimationFrame - } - return true - }) -} -func (s *TextureRenderMatrixSystem) Destroy() {} diff --git a/stdsystems/texture-render-spritesheet.go b/stdsystems/texture-render-spritesheet.go deleted file mode 100644 index 9c53b098..00000000 --- a/stdsystems/texture-render-spritesheet.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewTextureRenderSpriteSheetSystem() TextureRenderSpriteSheetSystem { - return TextureRenderSpriteSheetSystem{} -} - -// TextureRenderSpriteSheetSystem is a system that prepares SpriteSheet to be rendered -type TextureRenderSpriteSheetSystem struct { - SpriteSheets *stdcomponents.SpriteSheetComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager -} - -func (s *TextureRenderSpriteSheetSystem) Init() {} -func (s *TextureRenderSpriteSheetSystem) Run() { - s.SpriteSheets.AllParallel(func(entity ecs.Entity, spriteSheet *stdcomponents.SpriteSheet) bool { - if spriteSheet == nil { - return true - } - - tr := s.TextureRenders.Get(entity) - if tr == nil { - // Create new spriteRender - newRender := stdcomponents.TextureRender{ - Texture: spriteSheet.Texture, - Frame: spriteSheet.Frame, - Origin: spriteSheet.Origin, - Dest: rl.NewRectangle( - 0, - 0, - spriteSheet.Frame.Width, - spriteSheet.Frame.Height, - ), - } - - s.TextureRenders.Create(entity, newRender) - } else { - // Run spriteRender - tr.Texture = spriteSheet.Texture - tr.Frame = spriteSheet.Frame - tr.Origin = spriteSheet.Origin - } - return true - }) -} -func (s *TextureRenderSpriteSheetSystem) Destroy() {} diff --git a/stdsystems/texture-render-tint.go b/stdsystems/texture-render-tint.go deleted file mode 100644 index 7c027f83..00000000 --- a/stdsystems/texture-render-tint.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewTextureRenderTintSystem() TextureRenderTintSystem { - return TextureRenderTintSystem{} -} - -// TextureRenderTintSystem is a system that sets Scale of textureRender -type TextureRenderTintSystem struct { - Tints *stdcomponents.TintComponentManager - TextureRenders *stdcomponents.TextureRenderComponentManager -} - -func (s *TextureRenderTintSystem) Init() {} -func (s *TextureRenderTintSystem) Run() { - s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { - if tr == nil { - return true - } - - tint := s.Tints.Get(entity) - if tint == nil { - return true - } - - trTint := &tr.Tint - trTint.A = tint.A - trTint.R = tint.R - trTint.G = tint.G - trTint.B = tint.B - - return true - }) -} -func (s *TextureRenderTintSystem) Destroy() {} From debd54af5bbd3b7641785862e332fb596f7304d8 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 28 Feb 2025 20:36:35 +0300 Subject: [PATCH 027/196] sdl rulit --- examples/new-api/game.go | 2 +- internal/sdl3-cgo/sdl3cgo-game.go | 63 ++++++++++++++++++++++++++----- stdsystems/rl-render.go | 10 +++-- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 5e8efd2d..5e496780 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -28,5 +28,5 @@ func main() { game.CurrentSceneId = scenes.MainSceneId engine := gomp.NewEngine(&game) - engine.Run(20, 0) + engine.Run(50, 0) } diff --git a/internal/sdl3-cgo/sdl3cgo-game.go b/internal/sdl3-cgo/sdl3cgo-game.go index 70636c15..7613e170 100644 --- a/internal/sdl3-cgo/sdl3cgo-game.go +++ b/internal/sdl3-cgo/sdl3cgo-game.go @@ -15,34 +15,79 @@ Thank you for your support! package main import "C" -import "github.com/jfreymuth/go-sdl3/sdl" +import ( + "fmt" + "github.com/jfreymuth/go-sdl3/sdl" + "runtime" + "time" +) + +const batchSize = 1 << 14 +const rectCounter = 1 << 18 +const framerate = 600 func main() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + must(sdl.Init(sdl.InitVideo)) defer sdl.Quit() - w, r, e := sdl.CreateWindowAndRenderer("sld c-go", 640, 480, sdl.WindowResizable) + w, r, e := sdl.CreateWindowAndRenderer("sld c-go", 640, 480, sdl.WindowVulkan) must(e) defer w.Destroy() defer r.Destroy() + var renderTicker *time.Ticker + if framerate > 0 { + renderTicker = time.NewTicker(time.Second / time.Duration(framerate)) + defer renderTicker.Stop() + } + + var dt time.Duration = time.Second + var fps float64 + updatedAt := time.Now() + + rects := make([]sdl.FRect, rectCounter) + for i := range rects { + rects[i] = sdl.FRect{X: 300, Y: 300, W: 10, H: 10} + } + Outer: for { + if renderTicker != nil { + <-renderTicker.C + } + fps = (time.Second.Seconds() / dt.Seconds()) + dt = time.Since(updatedAt) + updatedAt = time.Now() + var event sdl.Event - sdl.PollEvent(&event) - switch event.Type() { - case sdl.EventQuit: - break Outer - case sdl.EventKeyDown: - if event.Keyboard().Scancode == sdl.ScancodeEscape { + for sdl.PollEvent(&event) { + switch event.Type() { + case sdl.EventQuit: break Outer + case sdl.EventKeyDown: + if event.Keyboard().Scancode == sdl.ScancodeEscape { + break Outer + } } } must(r.SetDrawColor(0, 0, 0, 255)) must(r.Clear()) + must(r.SetDrawColor(255, 255, 255, 255)) - must(r.DebugText(10, 10, "Hello")) + + for i := 0; i < len(rects); i += batchSize { + must(r.FillRects(rects[i : i+batchSize])) + } + + //must(r.FillRects(rects)) + + must(r.DebugText(10, 10, fmt.Sprintf("FPS %f", fps))) + must(r.DebugText(10, 20, fmt.Sprintf("dt %s", dt.String()))) + must(r.Present()) } } diff --git a/stdsystems/rl-render.go b/stdsystems/rl-render.go index 57546575..1db3da8a 100644 --- a/stdsystems/rl-render.go +++ b/stdsystems/rl-render.go @@ -143,16 +143,18 @@ func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() - dts := dt.Seconds() + //dts := dt.Seconds() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) position := s.Positions.Get(entity) if position == nil { return true } - decay := 40.0 // DECAY IS TICKRATE DEPENDENT - texturePro.Dest.X = float32(s.expDecay(float64(texturePro.Dest.X), float64(position.X), decay, dts)) - texturePro.Dest.Y = float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.Y), decay, dts)) + //decay := 16.0 // DECAY IS TICKRATE DEPENDENT + //texturePro.Dest.X = float32(s.expDecay(float64(texturePro.Dest.X), float64(position.X), decay, dts)) + //texturePro.Dest.Y = float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.Y), decay, dts)) + texturePro.Dest.X = position.X + texturePro.Dest.Y = position.Y return true }) From e38f5c9564ee8f8030553120eda1d664512a501b Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 1 Mar 2025 12:19:26 +0300 Subject: [PATCH 028/196] add HromRu@twitch to the history B) --- engine.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine.go b/engine.go index d02cbdf4..1466f6b0 100644 --- a/engine.go +++ b/engine.go @@ -7,7 +7,8 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- HromRu Donated 2500 RUB +<- HromRu Donated 5000 RUB Thank you for your support! */ From e3b48986e27958a89482cbdd54c7e948a0f5e286 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 3 Mar 2025 17:47:20 +0300 Subject: [PATCH 029/196] feat component-manager-shared.go --- examples/new-api/entities/player.go | 34 +--- examples/new-api/sprites/ids.go | 22 +++ examples/new-api/sprites/player.go | 50 +++++ examples/new-api/systems/player.go | 3 + internal/sdl3-cgo/sdl3cgo-game.go | 2 +- pkg/ecs/component-manager-shared.go | 270 +++++++++++++++++++++++++++ pkg/ecs/component-manager.go | 197 ++++++++----------- pkg/ecs/paged-array.go | 10 + pkg/ecs/paged-map.go | 6 +- stdcomponents/sprite-matrix.go | 4 +- stdsystems/animation-spritematrix.go | 6 +- stdsystems/sprite-sheet.go | 2 +- stdsystems/sprite.go | 2 +- stdsystems/velocity.go | 6 - 14 files changed, 449 insertions(+), 165 deletions(-) create mode 100644 examples/new-api/sprites/ids.go create mode 100644 examples/new-api/sprites/player.go create mode 100644 pkg/ecs/component-manager-shared.go diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index 2cc120d1..f507f6c4 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -7,8 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package entities import ( - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/examples/new-api/assets" + "gomp/examples/new-api/sprites" "gomp/pkg/ecs" "gomp/stdcomponents" ) @@ -37,35 +36,6 @@ type Player struct { Renderable *stdcomponents.Renderable } -var playerSpriteMatrix = stdcomponents.SpriteMatrix{ - Texture: assets.Textures.Get("milansheet.png"), - Origin: rl.Vector2{X: 0.5, Y: 0.5}, - FPS: 12, - Animations: []stdcomponents.SpriteMatrixAnimation{ - { - Name: "idle", - Frame: rl.Rectangle{X: 0, Y: 0, Width: 96, Height: 128}, - NumOfFrames: 1, - Vertical: false, - Loop: true, - }, - { - Name: "walk", - Frame: rl.Rectangle{X: 0, Y: 512, Width: 96, Height: 128}, - NumOfFrames: 8, - Vertical: false, - Loop: true, - }, - { - Name: "jump", - Frame: rl.Rectangle{X: 96, Y: 0, Width: 96, Height: 128}, - NumOfFrames: 1, - Vertical: false, - Loop: false, - }, - }, -} - func CreatePlayer( world *ecs.EntityManager, spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, @@ -108,7 +78,7 @@ func CreatePlayer( player.Tint = tints.Create(entity, tint) // Adding sprite matrix component - player.SpriteMatrix = spriteMatrixes.Create(entity, playerSpriteMatrix) + player.SpriteMatrix = spriteMatrixes.Set(entity, sprites.PlayerSpriteSharedComponentId) // Adding animation player component animation := stdcomponents.AnimationPlayer{} diff --git a/examples/new-api/sprites/ids.go b/examples/new-api/sprites/ids.go new file mode 100644 index 00000000..3f57782b --- /dev/null +++ b/examples/new-api/sprites/ids.go @@ -0,0 +1,22 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package sprites + +import "gomp/pkg/ecs" + +const ( + InvalidSharedComponentId ecs.SharedComponentInstanceId = iota + PlayerSpriteSharedComponentId +) diff --git a/examples/new-api/sprites/player.go b/examples/new-api/sprites/player.go new file mode 100644 index 00000000..8ac88363 --- /dev/null +++ b/examples/new-api/sprites/player.go @@ -0,0 +1,50 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package sprites + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/assets" + "gomp/stdcomponents" +) + +var PlayerSpriteMatrix = stdcomponents.SpriteMatrix{ + Texture: assets.Textures.Get("milansheet.png"), + Origin: rl.Vector2{X: 0.5, Y: 0.5}, + FPS: 12, + Animations: []stdcomponents.SpriteMatrixAnimation{ + { + Name: "idle", + Frame: rl.Rectangle{X: 0, Y: 0, Width: 96, Height: 128}, + NumOfFrames: 1, + Vertical: false, + Loop: true, + }, + { + Name: "walk", + Frame: rl.Rectangle{X: 0, Y: 512, Width: 96, Height: 128}, + NumOfFrames: 8, + Vertical: false, + Loop: true, + }, + { + Name: "jump", + Frame: rl.Rectangle{X: 96, Y: 0, Width: 96, Height: 128}, + NumOfFrames: 1, + Vertical: false, + Loop: false, + }, + }, +} diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index f4ce22c4..1283946c 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -10,6 +10,7 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/examples/new-api/components" "gomp/examples/new-api/entities" + "gomp/examples/new-api/sprites" "gomp/pkg/ecs" "gomp/stdcomponents" "math/rand" @@ -37,6 +38,8 @@ type PlayerSystem struct { } func (s *PlayerSystem) Init() { + s.SpriteMatrixes.Create(sprites.PlayerSpriteSharedComponentId, sprites.PlayerSpriteMatrix) + for range 20_000 { s.Player = entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, diff --git a/internal/sdl3-cgo/sdl3cgo-game.go b/internal/sdl3-cgo/sdl3cgo-game.go index 7613e170..a06353fb 100644 --- a/internal/sdl3-cgo/sdl3cgo-game.go +++ b/internal/sdl3-cgo/sdl3cgo-game.go @@ -33,7 +33,7 @@ func main() { must(sdl.Init(sdl.InitVideo)) defer sdl.Quit() - w, r, e := sdl.CreateWindowAndRenderer("sld c-go", 640, 480, sdl.WindowVulkan) + w, r, e := sdl.CreateWindowAndRenderer("sld c-go", 2560, 1440, sdl.WindowResizable) must(e) defer w.Destroy() defer r.Destroy() diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go new file mode 100644 index 00000000..34483a4c --- /dev/null +++ b/pkg/ecs/component-manager-shared.go @@ -0,0 +1,270 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package ecs + +import ( + "sync" + + "github.com/negrel/assert" +) + +type SharedComponentInstanceId uint16 + +func NewSharedComponentManager[T any](id ComponentId) SharedComponentManager[T] { + newManager := SharedComponentManager[T]{ + components: NewPagedArray[T](), + instances: NewPagedArray[SharedComponentInstanceId](), + instanceToComponent: NewPagedMap[SharedComponentInstanceId, int](), + entityToComponent: NewPagedMap[Entity, int](), + entities: NewPagedArray[Entity](), + references: NewPagedArray[SharedComponentInstanceId](), + lookup: NewPagedMap[Entity, int](), + + id: id, + isInitialized: true, + + TrackChanges: false, + createdEntities: NewPagedArray[Entity](), + patchedEntities: NewPagedArray[Entity](), + deletedEntities: NewPagedArray[Entity](), + } + + return newManager +} + +type SharedComponentManager[T any] struct { + mx sync.Mutex + components PagedArray[T] + instances PagedArray[SharedComponentInstanceId] + instanceToComponent PagedMap[SharedComponentInstanceId, int] // value is components array index + entityToComponent PagedMap[Entity, int] + + // for itter returning 2 values + entities PagedArray[Entity] + references PagedArray[SharedComponentInstanceId] + lookup PagedMap[Entity, int] + + entityManager *EntityManager + entityComponentBitSet *ComponentBitSet + + id ComponentId + isInitialized bool + + // Patch + + TrackChanges bool // Enable TrackChanges to track changes and add them to patch + createdEntities PagedArray[Entity] + patchedEntities PagedArray[Entity] + deletedEntities PagedArray[Entity] + + encoder func([]T) []byte + decoder func([]byte) []T +} + +func (c *SharedComponentManager[T]) Id() ComponentId { + return c.id +} + +func (c *SharedComponentManager[T]) registerEntityManager(entityManager *EntityManager) { + c.entityManager = entityManager + c.entityComponentBitSet = &entityManager.componentBitSet +} + +//===================================== +//===================================== +//===================================== + +// Create an instance of shared component +func (c *SharedComponentManager[T]) Create(instanceId SharedComponentInstanceId, value T) *T { + c.mx.Lock() + defer c.mx.Unlock() + + c.assertBegin() + defer c.assertEnd() + + componentIndex := c.components.Len() + component := c.components.Append(value) + c.instances.Append(instanceId) + c.instanceToComponent.Set(instanceId, componentIndex) + + return component +} + +func (c *SharedComponentManager[T]) Get(entity Entity) (component *T) { + assert.True(c.isInitialized, "SharedComponentManager should be created with SharedNewComponentManager()") + index, exists := c.entityToComponent.Get(entity) + if !exists { + return nil + } + component = c.components.Get(index) + return component +} + +func (c *SharedComponentManager[T]) GetComponentByInstance(instanceId SharedComponentInstanceId) (component *T) { + assert.True(c.isInitialized, "SharedComponentManager should be created with SharedNewComponentManager()") + index, exists := c.instanceToComponent.Get(instanceId) + if !exists { + return nil + } + component = c.components.Get(index) + return component +} + +func (c *SharedComponentManager[T]) GetInstanceByEntity(entity Entity) (SharedComponentInstanceId, bool) { + assert.True(c.isInitialized, "SharedComponentManager should be created with SharedNewComponentManager()") + index, exists := c.lookup.Get(entity) + if !exists { + return 0, false + } + instanceId := *c.instances.Get(index) + return instanceId, true +} + +func (c *SharedComponentManager[T]) Set(entity Entity, instanceId SharedComponentInstanceId) *T { + assert.True(c.isInitialized, "SharedComponentManager should be created with SharedNewComponentManager()") + index, exists := c.lookup.Get(entity) + if exists { + c.references.Set(index, instanceId) + } else { + newIndex := c.entities.Len() + c.entities.Append(entity) + c.references.Append(instanceId) + c.lookup.Set(entity, newIndex) + } + componentIndex, _ := c.instanceToComponent.Get(instanceId) + c.entityToComponent.Set(entity, componentIndex) + c.patchedEntities.Append(entity) + return c.components.Get(componentIndex) +} + +func (c *SharedComponentManager[T]) Remove(entity Entity) { + c.mx.Lock() + defer c.mx.Unlock() + + c.assertBegin() + defer c.assertEnd() + + index, _ := c.lookup.Get(entity) + + lastIndex := c.references.Len() - 1 + if index < lastIndex { + // Swap the dead element with the last one + c.references.Swap(index, lastIndex) + newSwappedEntityId, _ := c.entities.Swap(index, lastIndex) + assert.True(newSwappedEntityId != nil) + + // Update the lookup table + c.lookup.Set(*newSwappedEntityId, index) + } + + // Shrink the container + c.references.SoftReduce() + c.entities.SoftReduce() + + c.lookup.Delete(entity) + c.entityToComponent.Delete(entity) + c.entityComponentBitSet.Unset(entity, c.id) + + c.deletedEntities.Append(entity) +} + +func (c *SharedComponentManager[T]) Has(entity Entity) bool { + _, ok := c.lookup.Get(entity) + return ok +} + +func (c *SharedComponentManager[T]) Len() int { + assert.True(c.isInitialized, "SharedComponentManager should be created with CreateComponentService()") + return c.entities.Len() +} + +func (c *SharedComponentManager[T]) Clean() { + // c.entityComponentBitSet.Clean() + //c.components.Clean() + // c.entities.Clean() +} + +// ======================================================== +// Iterators +// ======================================================== + +func (c *SharedComponentManager[T]) EachComponent(yield func(*T) bool) { + c.assertBegin() + defer c.assertEnd() + c.components.AllData(yield) +} + +func (c *SharedComponentManager[T]) EachEntity(yield func(Entity) bool) { + c.assertBegin() + defer c.assertEnd() + c.entities.AllDataValue(yield) +} + +func (c *SharedComponentManager[T]) Each(yield func(Entity, *T) bool) { + c.assertBegin() + defer c.assertEnd() + c.components.All(func(i int, d *T) bool { + entity := c.entities.Get(i) + entId := *entity + shouldContinue := yield(entId, d) + return shouldContinue + }) +} + +// ======================================================== +// Iterators Parallel +// ======================================================== + +func (c *SharedComponentManager[T]) EachComponentParallel(yield func(*T) bool) { + c.assertBegin() + defer c.assertEnd() + c.components.AllDataParallel(yield) +} + +func (c *SharedComponentManager[T]) EachEntityParallel(yield func(Entity) bool) { + c.assertBegin() + defer c.assertEnd() + c.entities.AllDataValueParallel(yield) +} + +func (c *SharedComponentManager[T]) EachParallel(yield func(Entity, *T) bool) { + c.assertBegin() + defer c.assertEnd() + c.components.AllParallel(func(i int, t *T) bool { + entity := c.entities.Get(i) + entId := *entity + shouldContinue := yield(entId, t) + return shouldContinue + }) +} + +// ======================================================== +// Patches +// ======================================================== + +func (c *SharedComponentManager[T]) IsTrackingChanges() bool { + return c.TrackChanges +} + +// ======================================================== +// Utils +// ======================================================== + +func (c *SharedComponentManager[T]) RawComponents(ptr []T) { + c.components.Raw(ptr) +} + +func (c *SharedComponentManager[T]) assertBegin() { + assert.True(c.isInitialized, "SharedComponentManager should be created with SharedNewComponentManager()") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} + +func (c *SharedComponentManager[T]) assertEnd() { + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 39572832..39583fb0 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -110,10 +110,9 @@ func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { c.mx.Lock() defer c.mx.Unlock() - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") assert.False(c.Has(entity), "Only one of component per entity allowed!") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + c.assertBegin() + defer c.assertEnd() var index = c.components.Len() @@ -129,7 +128,7 @@ func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { } func (c *ComponentManager[T]) Get(entity Entity) (component *T) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.isInitialized, "ComponentManager should be created with NewComponentManager()") index, ok := c.lookup.Get(entity) if !ok { @@ -140,7 +139,7 @@ func (c *ComponentManager[T]) Get(entity Entity) (component *T) { } func (c *ComponentManager[T]) Set(entity Entity, value T) *T { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.isInitialized, "ComponentManager should be created with NewComponentManager()") index, ok := c.lookup.Get(entity) if !ok { @@ -158,9 +157,8 @@ func (c *ComponentManager[T]) Remove(entity Entity) { c.mx.Lock() defer c.mx.Unlock() - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + c.assertBegin() + defer c.assertEnd() index, _ := c.lookup.Get(entity) @@ -183,9 +181,6 @@ func (c *ComponentManager[T]) Remove(entity Entity) { c.entityComponentBitSet.Unset(entity, c.id) c.deletedEntities.Append(entity) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") } func (c *ComponentManager[T]) Has(entity Entity) bool { @@ -193,24 +188,88 @@ func (c *ComponentManager[T]) Has(entity Entity) bool { return ok } +func (c *ComponentManager[T]) Len() int { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + return c.components.Len() +} + +func (c *ComponentManager[T]) Clean() { + // c.entityComponentBitSet.Clean() + //c.components.Clean() + // c.entities.Clean() +} + +// ======================================================== +// Iterators +// ======================================================== + +func (c *ComponentManager[T]) EachComponent(yield func(*T) bool) { + c.assertBegin() + defer c.assertEnd() + c.components.AllData(yield) +} + +func (c *ComponentManager[T]) EachEntity(yield func(Entity) bool) { + c.assertBegin() + defer c.assertEnd() + c.entities.AllDataValue(yield) +} + +func (c *ComponentManager[T]) Each(yield func(Entity, *T) bool) { + c.assertBegin() + defer c.assertEnd() + c.components.All(func(i int, d *T) bool { + entity := c.entities.Get(i) + entId := *entity + shouldContinue := yield(entId, d) + return shouldContinue + }) +} + +// ======================================================== +// Iterators Parallel +// ======================================================== + +func (c *ComponentManager[T]) EachComponentParallel(yield func(*T) bool) { + c.assertBegin() + defer c.assertEnd() + c.components.AllDataParallel(yield) +} + +func (c *ComponentManager[T]) EachEntityParallel(yield func(Entity) bool) { + c.assertBegin() + defer c.assertEnd() + c.entities.AllDataValueParallel(yield) +} + +func (c *ComponentManager[T]) EachParallel(yield func(Entity, *T) bool) { + c.assertBegin() + defer c.assertEnd() + c.components.AllParallel(func(i int, t *T) bool { + entity := c.entities.Get(i) + entId := *entity + shouldContinue := yield(entId, t) + return shouldContinue + }) +} + +// ======================================================== // Patches +// ======================================================== func (c *ComponentManager[T]) PatchAdd(entity Entity) { assert.True(c.TrackChanges) - c.patchedEntities.Append(entity) } func (c *ComponentManager[T]) PatchGet() ComponentPatch { assert.True(c.TrackChanges) - patch := ComponentPatch{ ID: c.id, Created: c.getChangesBinary(&c.createdEntities), Patched: c.getChangesBinary(&c.patchedEntities), Deleted: c.getChangesBinary(&c.deletedEntities), } - return patch } @@ -242,7 +301,6 @@ func (c *ComponentManager[T]) PatchApply(patch ComponentPatch) { func (c *ComponentManager[T]) PatchReset() { assert.True(c.TrackChanges) - c.createdEntities.Reset() c.patchedEntities.Reset() c.deletedEntities.Reset() @@ -288,114 +346,21 @@ func (c *ComponentManager[T]) IsTrackingChanges() bool { return c.TrackChanges } -// Iterators - -func (c *ComponentManager[T]) EachComponent(yield func(*T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.AllData(yield) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -func (c *ComponentManager[T]) EachComponentParallel(yield func(*T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.AllDataParallel(yield) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -func (c *ComponentManager[T]) EachEntity(yield func(Entity) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.entities.AllDataValue(yield) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -func (c *ComponentManager[T]) EachEntityParallel(yield func(Entity) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.entities.AllDataValueParallel(yield) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -func (c *ComponentManager[T]) All(yield func(Entity, *T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.All(func(i int, d *T) bool { - assert.True(d != nil) - entity := c.entities.Get(i) - assert.True(entity != nil) - entId := *entity - shouldContinue := yield(entId, d) - return shouldContinue - }) +// ======================================================== +// Utils +// ======================================================== - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +func (c *ComponentManager[T]) RawComponents(ptr []T) { + c.components.Raw(ptr) } -func (c *ComponentManager[T]) AllParallel(yield func(Entity, *T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.AllParallel(func(i int, t *T) bool { - entity := c.entities.Get(i) - assert.True(entity != nil) - entId := *entity - shouldContinue := yield(entId, t) - return shouldContinue - }) - +func (c *ComponentManager[T]) assertBegin() { + assert.True(c.isInitialized, "ComponentManager should be created with NewComponentManager()") assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") } -func (c *ComponentManager[T]) AllDataParallel(yield func(*T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") +func (c *ComponentManager[T]) assertEnd() { assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.AllDataParallel(yield) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -// Utils - -func (c *ComponentManager[T]) RawComponents(ptr []T) { - c.components.Raw(ptr) -} - -func (c *ComponentManager[T]) Len() int { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - - return c.components.Len() -} - -func (c *ComponentManager[T]) Clean() { - // c.entityComponentBitSet.Clean() - // c.components.Clean() - // c.entities.Clean() } diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index aaa2fe84..447c2f06 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -41,6 +41,16 @@ func (a *PagedArray[T]) Get(index int) *T { return &(page.data[index]) } +func (a *PagedArray[T]) GetValue(index int) T { + assert.True(index >= 0, "index out of range") + assert.True(index < a.len, "index out of range") + + pageId, index := a.getPageIdAndIndex(index) + page := &a.book[pageId] + + return (page.data[index]) +} + func (a *PagedArray[T]) Set(index int, value T) *T { assert.True(index >= 0, "index out of range") assert.True(index < a.len, "index out of range") diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index bd8b7949..24a781a3 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -16,8 +16,8 @@ const ( book_size int = 1 << 2 ) -type MapPage[K Entity, V any] map[K]V -type PagedMap[K Entity, V any] struct { +type MapPage[K Entity | SharedComponentInstanceId, V any] map[K]V +type PagedMap[K Entity | SharedComponentInstanceId, V any] struct { len int book []SlicePage[MapValue[V]] } @@ -26,7 +26,7 @@ type MapValue[V any] struct { ok bool } -func NewPagedMap[K Entity, V any]() PagedMap[K, V] { +func NewPagedMap[K Entity | SharedComponentInstanceId, V any]() PagedMap[K, V] { return PagedMap[K, V]{ book: make([]SlicePage[MapValue[V]], book_size), } diff --git a/stdcomponents/sprite-matrix.go b/stdcomponents/sprite-matrix.go index 2d71e5f6..5db7fa96 100644 --- a/stdcomponents/sprite-matrix.go +++ b/stdcomponents/sprite-matrix.go @@ -34,8 +34,8 @@ type SpriteMatrix struct { Animations []SpriteMatrixAnimation } -type SpriteMatrixComponentManager = ecs.ComponentManager[SpriteMatrix] +type SpriteMatrixComponentManager = ecs.SharedComponentManager[SpriteMatrix] func NewSpriteMatrixComponentManager() SpriteMatrixComponentManager { - return ecs.NewComponentManager[SpriteMatrix](SpriteMatrixComponentId) + return ecs.NewSharedComponentManager[SpriteMatrix](SpriteMatrixComponentId) } diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index c4a976ea..cc5612b8 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -18,9 +18,9 @@ func NewAnimationSpriteMatrixSystem() AnimationSpriteMatrixSystem { type AnimationSpriteMatrixSystem struct { World *ecs.EntityManager - AnimationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] - AnimationStates *ecs.ComponentManager[stdcomponents.AnimationState] - SpriteMatrixes *ecs.ComponentManager[stdcomponents.SpriteMatrix] + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager } func (s *AnimationSpriteMatrixSystem) Init() {} diff --git a/stdsystems/sprite-sheet.go b/stdsystems/sprite-sheet.go index 9166f737..0bc6f8a9 100644 --- a/stdsystems/sprite-sheet.go +++ b/stdsystems/sprite-sheet.go @@ -22,7 +22,7 @@ type TextureRenderSpriteSheetSystem struct { func (s *TextureRenderSpriteSheetSystem) Init() {} func (s *TextureRenderSpriteSheetSystem) Run() { - //s.SpriteSheets.AllParallel(func(entity ecs.Entity, spriteSheet *stdcomponents.SpriteSheet) bool { + //s.SpriteSheets.EachParallel(func(entity ecs.Entity, spriteSheet *stdcomponents.SpriteSheet) bool { // if spriteSheet == nil { // return true // } diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index ccd36067..8969320e 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -31,7 +31,7 @@ type TextureRenderSpriteSystem struct { func (s *TextureRenderSpriteSystem) Init() {} func (s *TextureRenderSpriteSystem) Run() { // Run sprites and spriteRenders - //s.Sprites.AllParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { + //s.Sprites.EachParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { // if sprite == nil { // return true // } diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 54dbd10a..025efac8 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -24,12 +24,6 @@ type VelocitySystem struct { func (s *VelocitySystem) Init() {} func (s *VelocitySystem) Run(dt time.Duration) { dtSec := float32(dt.Seconds()) - //s.Velocities.AllParallel(func(entity ecs.Entity, v *stdcomponents.Velocity) bool { - // position := s.Positions.Get(entity) - // position.X += v.X * dtSec - // position.Y += v.Y * dtSec - // return true - //}) s.Velocities.EachEntity(func(e ecs.Entity) bool { velocity := s.Velocities.Get(e) position := s.Positions.Get(e) From 90ac2cda0c8ebb9b261504a674b6181a03b9f56f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 3 Mar 2025 20:56:08 +0300 Subject: [PATCH 030/196] feat new render.go with sprite sorting --- stdcomponents/position.go | 2 +- stdsystems/render.go | 252 +++++++++++++++++++++++++++++++++++++ stdsystems/rl-render.go | 34 ++--- stdsystems/sprite-sheet.go | 4 +- stdsystems/sprite.go | 4 +- 5 files changed, 274 insertions(+), 22 deletions(-) create mode 100644 stdsystems/render.go diff --git a/stdcomponents/position.go b/stdcomponents/position.go index d04f258c..b4dc177d 100644 --- a/stdcomponents/position.go +++ b/stdcomponents/position.go @@ -17,7 +17,7 @@ package stdcomponents import "gomp/pkg/ecs" type Position struct { - X, Y float32 + X, Y, Z float32 } type PositionComponentManager = ecs.ComponentManager[Position] diff --git a/stdsystems/render.go b/stdsystems/render.go new file mode 100644 index 00000000..af3fb5d3 --- /dev/null +++ b/stdsystems/render.go @@ -0,0 +1,252 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "fmt" + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "sort" + "sync" + "time" +) + +func NewRenderSystem() RenderSystem { + return RenderSystem{} +} + +type RenderSystem struct { + EntityManager *ecs.EntityManager + RlTexturePros *stdcomponents.RLTextureProComponentManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + Tints *stdcomponents.TintComponentManager + Flips *stdcomponents.FlipComponentManager + Renderables *stdcomponents.RenderableComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + renderList []RenderEntry + instanceData []stdcomponents.RLTexturePro + camera rl.Camera2D +} + +type RenderEntry struct { + Entity ecs.Entity + TextureId int + ZIndex float32 +} + +func (s *RenderSystem) Init() { + rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") + //InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") + + s.camera = rl.Camera2D{ + Target: rl.NewVector2(0, 0), + Offset: rl.NewVector2(0, 0), + Rotation: 0, + Zoom: 1, + } +} +func (s *RenderSystem) Run(dt time.Duration) bool { + if rl.WindowShouldClose() { + return false + } + + s.prepareRender(dt) + + rl.BeginDrawing() + rl.ClearBackground(rl.Black) + s.render() + rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) + rl.DrawFPS(10, 10) + rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) + rl.EndDrawing() + + return true +} + +func (s *RenderSystem) Destroy() { + rl.CloseWindow() +} + +func (s *RenderSystem) render() { + // Extract and sort entities + if cap(s.renderList) < s.Renderables.Len() { + s.renderList = append(s.renderList, make([]RenderEntry, 0, s.Renderables.Len()-cap(s.renderList))...) + } + s.Renderables.EachEntity(func(e ecs.Entity) bool { + sprite := s.SpriteMatrixes.Get(e) + pos := s.Positions.Get(e) + s.renderList = append(s.renderList, RenderEntry{ + Entity: e, + TextureId: int(sprite.Texture.ID), + ZIndex: pos.Z, + }) + return true + }) + + // Sort by texture, then Z-index + sort.SliceStable(s.renderList, func(i, j int) bool { + a := s.renderList[i] + b := s.renderList[j] + if a.TextureId == b.TextureId { + return a.ZIndex < b.ZIndex + } + return a.TextureId < b.TextureId + }) + + // Batch and render + var currentTex = -1 + var instanceData []stdcomponents.RLTexturePro = make([]stdcomponents.RLTexturePro, 0, 8192) + for i := range s.renderList { + entry := &s.renderList[i] + if entry.TextureId != currentTex || len(instanceData) >= 8192 { + if len(instanceData) > 0 { + s.submitBatch(currentTex, instanceData) + instanceData = instanceData[:0] + } + currentTex = entry.TextureId + } + instanceData = append(instanceData, s.getInstanceData(entry.Entity)) + } + s.submitBatch(currentTex, instanceData) // Submit last batch + s.renderList = s.renderList[:0] +} + +func (s *RenderSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { + return *s.RlTexturePros.Get(e) +} + +func (s *RenderSystem) prepareRender(dt time.Duration) { + wg := new(sync.WaitGroup) + wg.Add(6) + s.prepareAnimations(wg) + go s.prepareFlips(wg) + go s.preparePositions(wg, dt) + go s.prepareRotations(wg) + go s.prepareScales(wg) + go s.prepareTints(wg) + wg.Wait() +} + +func (s *RenderSystem) prepareAnimations(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + animation := s.AnimationPlayers.Get(entity) + if animation == nil { + return true + } + frame := &texturePro.Frame + if animation.Vertical { + frame.Y += frame.Height * float32(animation.Current) + } else { + frame.X += frame.Width * float32(animation.Current) + } + return true + }) +} + +func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + mirrored := s.Flips.Get(entity) + if mirrored == nil { + return true + } + if mirrored.X { + texturePro.Frame.Width *= -1 + } + if mirrored.Y { + texturePro.Frame.Height *= -1 + } + return true + }) +} + +func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { + defer wg.Done() + //dts := dt.Seconds() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + position := s.Positions.Get(entity) + if position == nil { + return true + } + //decay := 16.0 // DECAY IS TICKRATE DEPENDENT + //texturePro.Dest.X = float32(s.expDecay(float64(texturePro.Dest.X), float64(position.X), decay, dts)) + //texturePro.Dest.Y = float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.Y), decay, dts)) + texturePro.Dest.X = position.X + texturePro.Dest.Y = position.Y + + return true + }) +} + +func (s *RenderSystem) prepareRotations(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + rotation := s.Rotations.Get(entity) + if rotation == nil { + return true + } + texturePro.Rotation = rotation.Angle + return true + }) +} + +func (s *RenderSystem) prepareScales(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + scale := s.Scales.Get(entity) + if scale == nil { + return true + } + texturePro.Dest.Width *= scale.X + texturePro.Dest.Height *= scale.Y + return true + }) +} + +func (s *RenderSystem) prepareTints(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + tr := s.RlTexturePros.Get(entity) + tint := s.Tints.Get(entity) + if tint == nil { + return true + } + trTint := &tr.Tint + trTint.A = tint.A + trTint.R = tint.R + trTint.G = tint.G + trTint.B = tint.B + return true + }) +} + +func (s *RenderSystem) submitBatch(texID int, data []stdcomponents.RLTexturePro) { + rl.BeginMode2D(s.camera) + for i := range data { + rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) + } + rl.EndMode2D() +} diff --git a/stdsystems/rl-render.go b/stdsystems/rl-render.go index 1db3da8a..20e94ae4 100644 --- a/stdsystems/rl-render.go +++ b/stdsystems/rl-render.go @@ -20,11 +20,11 @@ const ( batchSize = 1 << 13 // Maximum batch size supported by Raylib ) -func NewRenderSystem() RenderSystem { - return RenderSystem{} +func NewRlRenderSystem() RlRenderSystem { + return RlRenderSystem{} } -type RenderSystem struct { +type RlRenderSystem struct { EntityManager *ecs.EntityManager RlTexturePros *stdcomponents.RLTextureProComponentManager Positions *stdcomponents.PositionComponentManager @@ -39,7 +39,7 @@ type RenderSystem struct { camera rl.Camera2D } -func (s *RenderSystem) Init() { +func (s *RlRenderSystem) Init() { rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") //InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") @@ -51,7 +51,7 @@ func (s *RenderSystem) Init() { } } -func (s *RenderSystem) Run(dt time.Duration) bool { +func (s *RlRenderSystem) Run(dt time.Duration) bool { if rl.WindowShouldClose() { return false } @@ -62,7 +62,7 @@ func (s *RenderSystem) Run(dt time.Duration) bool { rl.ClearBackground(rl.Black) rl.BeginMode2D(s.camera) - s.renderWorld() + s.render() rl.EndMode2D() rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) @@ -74,11 +74,11 @@ func (s *RenderSystem) Run(dt time.Duration) bool { return true } -func (s *RenderSystem) Destroy() { +func (s *RlRenderSystem) Destroy() { rl.CloseWindow() } -func (s *RenderSystem) renderWorld() { +func (s *RlRenderSystem) render() { s.Renderables.EachEntity(func(entity ecs.Entity) bool { renderable := s.Renderables.Get(entity) @@ -93,7 +93,7 @@ func (s *RenderSystem) renderWorld() { }) } -func (s *RenderSystem) prepareRender(dt time.Duration) { +func (s *RlRenderSystem) prepareRender(dt time.Duration) { wg := new(sync.WaitGroup) wg.Add(6) s.prepareAnimations(wg) @@ -105,7 +105,7 @@ func (s *RenderSystem) prepareRender(dt time.Duration) { wg.Wait() } -func (s *RenderSystem) prepareAnimations(wg *sync.WaitGroup) { +func (s *RlRenderSystem) prepareAnimations(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) @@ -123,7 +123,7 @@ func (s *RenderSystem) prepareAnimations(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { +func (s *RlRenderSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) @@ -141,7 +141,7 @@ func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { +func (s *RlRenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() //dts := dt.Seconds() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { @@ -160,7 +160,7 @@ func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { }) } -func (s *RenderSystem) prepareRotations(wg *sync.WaitGroup) { +func (s *RlRenderSystem) prepareRotations(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) @@ -173,7 +173,7 @@ func (s *RenderSystem) prepareRotations(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) prepareScales(wg *sync.WaitGroup) { +func (s *RlRenderSystem) prepareScales(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) @@ -187,7 +187,7 @@ func (s *RenderSystem) prepareScales(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) prepareTints(wg *sync.WaitGroup) { +func (s *RlRenderSystem) prepareTints(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { tr := s.RlTexturePros.Get(entity) @@ -204,11 +204,11 @@ func (s *RenderSystem) prepareTints(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) renderSpriteMatrix(entity ecs.Entity) { +func (s *RlRenderSystem) renderSpriteMatrix(entity ecs.Entity) { texturePro := s.RlTexturePros.Get(entity) rl.DrawTexturePro(*texturePro.Texture, texturePro.Frame, texturePro.Dest, texturePro.Origin, texturePro.Rotation, texturePro.Tint) } -func (s *RenderSystem) expDecay(a, b, decay, dt float64) float64 { +func (s *RlRenderSystem) expDecay(a, b, decay, dt float64) float64 { return b + (a-b)*(math.Exp(-decay*dt)) } diff --git a/stdsystems/sprite-sheet.go b/stdsystems/sprite-sheet.go index 0bc6f8a9..2410b7bf 100644 --- a/stdsystems/sprite-sheet.go +++ b/stdsystems/sprite-sheet.go @@ -31,7 +31,7 @@ func (s *TextureRenderSpriteSheetSystem) Run() { // if tr == nil { // // Create new spriteRender // newRender := stdcomponents.RLTexturePro{ - // Texture: spriteSheet.Texture, + // TextureId: spriteSheet.TextureId, // Frame: spriteSheet.Frame, // Origin: spriteSheet.Origin, // Dest: rl.NewRectangle( @@ -45,7 +45,7 @@ func (s *TextureRenderSpriteSheetSystem) Run() { // s.RlTexturePros.Create(entity, newRender) // } else { // // Run spriteRender - // tr.Texture = spriteSheet.Texture + // tr.TextureId = spriteSheet.TextureId // tr.Frame = spriteSheet.Frame // tr.Origin = spriteSheet.Origin // } diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index 8969320e..f906d210 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -44,7 +44,7 @@ func (s *TextureRenderSpriteSystem) Run() { // if tr == nil { // // Create new spriteRender // newRender := stdcomponents.RLTexturePro{ - // Texture: sprite.Texture, + // TextureId: sprite.TextureId, // Frame: sprite.Frame, // Origin: sprite.Origin, // Tint: sprite.Tint, @@ -59,7 +59,7 @@ func (s *TextureRenderSpriteSystem) Run() { // s.RlTexturePros.Create(entity, newRender) // } else { // // Run spriteRender - // // tr.Texture = sprite.Texture + // // tr.TextureId = sprite.TextureId // trFrame := &tr.Frame // trFrame.X = spriteFrame.X // trFrame.Y = spriteFrame.Y From acd7633a2b3827c1e1f7c9806329a6295c3b0f12 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 5 Mar 2025 14:46:36 +0300 Subject: [PATCH 031/196] feat ysort system and component --- examples/new-api/entities/player.go | 10 ++++ examples/new-api/instances/component-list.go | 8 ++-- examples/new-api/instances/system-list.go | 2 + examples/new-api/scenes/main-scene.go | 3 ++ examples/new-api/systems/player.go | 3 ++ stdcomponents/ids.go | 2 + stdcomponents/render-order.go | 27 +++++++++++ stdcomponents/ysort.go | 26 ++++++++++ stdsystems/render.go | 17 ++++--- stdsystems/ysort.go | 50 ++++++++++++++++++++ 10 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 stdcomponents/render-order.go create mode 100644 stdcomponents/ysort.go create mode 100644 stdsystems/ysort.go diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index f507f6c4..a9744ac0 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -34,6 +34,8 @@ type Player struct { AnimationState *stdcomponents.AnimationState Flip *stdcomponents.Flip Renderable *stdcomponents.Renderable + YSort *stdcomponents.YSort + RenderOrder *stdcomponents.RenderOrder } func CreatePlayer( @@ -48,6 +50,8 @@ func CreatePlayer( tints *stdcomponents.TintComponentManager, flips *stdcomponents.FlipComponentManager, renderables *stdcomponents.RenderableComponentManager, + ySorts *stdcomponents.YSortComponentManager, + renderOrders *stdcomponents.RenderOrderComponentManager, ) (player Player) { // Creating new player @@ -93,5 +97,11 @@ func CreatePlayer( // Adding renderable component player.Renderable = renderables.Create(entity, stdcomponents.SpriteMatrixRenderableType) + // Adding YSort component + player.YSort = ySorts.Create(entity, stdcomponents.YSort{}) + + // Adding RenderOrder component + player.RenderOrder = renderOrders.Create(entity, stdcomponents.RenderOrder{}) + return player } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index 653e7e7a..e178443c 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -25,8 +25,6 @@ type ComponentList struct { Scale stdcomponents.ScaleComponentManager Velocity stdcomponents.VelocityComponentManager Flip stdcomponents.FlipComponentManager - Sprite stdcomponents.SpriteComponentManager - SpriteSheet stdcomponents.SpriteSheetComponentManager SpriteMatrix stdcomponents.SpriteMatrixComponentManager Tint stdcomponents.TintComponentManager AnimationPlayer stdcomponents.AnimationPlayerComponentManager @@ -34,6 +32,8 @@ type ComponentList struct { RLTexturePro stdcomponents.RLTextureProComponentManager Network stdcomponents.NetworkComponentManager Renderable stdcomponents.RenderableComponentManager + YSort stdcomponents.YSortComponentManager + RenderOrder stdcomponents.RenderOrderComponentManager Health components.HealthComponentManager Controller components.ControllerComponentManager @@ -46,8 +46,6 @@ func NewComponentList() ComponentList { Scale: stdcomponents.NewScaleComponentManager(), Velocity: stdcomponents.NewVelocityComponentManager(), Flip: stdcomponents.NewFlipComponentManager(), - Sprite: stdcomponents.NewSpriteComponentManager(), - SpriteSheet: stdcomponents.NewSpriteSheetComponentManager(), SpriteMatrix: stdcomponents.NewSpriteMatrixComponentManager(), Tint: stdcomponents.NewTintComponentManager(), AnimationPlayer: stdcomponents.NewAnimationPlayerComponentManager(), @@ -55,6 +53,8 @@ func NewComponentList() ComponentList { RLTexturePro: stdcomponents.NewRlTextureProComponentManager(), Network: stdcomponents.NewNetworkComponentManager(), Renderable: stdcomponents.NewRenderableComponentManager(), + YSort: stdcomponents.NewYSortComponentManager(), + RenderOrder: stdcomponents.NewRenderOrderComponentManager(), Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index 44bfac8b..66af7b96 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -35,6 +35,7 @@ func NewSystemList() SystemList { TextureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(), SpriteMatrix: stdsystems.NewSpriteMatrixSystem(), AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{assets.Textures}), + YSort: stdsystems.NewYSortSystem(), Render: stdsystems.NewRenderSystem(), } @@ -54,5 +55,6 @@ type SystemList struct { TextureRenderSpriteSheet stdsystems.TextureRenderSpriteSheetSystem SpriteMatrix stdsystems.SpriteMatrixSystem AssetLib stdsystems.AssetLibSystem + YSort stdsystems.YSortSystem Render stdsystems.RenderSystem } diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index d3937636..aa261f99 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -55,6 +55,7 @@ func (s *MainScene) Init() { s.World.Systems.AnimationPlayer.Init() s.World.Systems.SpriteMatrix.Init() + s.World.Systems.YSort.Init() // Render s.World.Systems.Render.Init() @@ -86,6 +87,7 @@ func (s *MainScene) Render(dt time.Duration) { s.World.Systems.SpriteMatrix.Run() s.World.Systems.Debug.Run() s.World.Systems.AssetLib.Run() + s.World.Systems.YSort.Run() shouldContinue := s.World.Systems.Render.Run(dt) if !shouldContinue { @@ -110,6 +112,7 @@ func (s *MainScene) Destroy() { s.World.Systems.AnimationPlayer.Destroy() s.World.Systems.SpriteMatrix.Destroy() + s.World.Systems.YSort.Destroy() // Render s.World.Systems.Debug.Destroy() diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 1283946c..a6ae9a18 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -35,6 +35,8 @@ type PlayerSystem struct { HP *components.HealthComponentManager Controllers *components.ControllerComponentManager Renderables *stdcomponents.RenderableComponentManager + YSorts *stdcomponents.YSortComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager } func (s *PlayerSystem) Init() { @@ -44,6 +46,7 @@ func (s *PlayerSystem) Init() { s.Player = entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, + s.YSorts, s.RenderOrders, ) s.Player.Position.X = 100 + rand.Float32()*700 diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 98b7c615..6bdc5e33 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -26,5 +26,7 @@ const ( TintComponentId NetworkComponentId RenderableComponentId + YSortComponentId + RenderOrderComponentId StdComponentIds ) diff --git a/stdcomponents/render-order.go b/stdcomponents/render-order.go new file mode 100644 index 00000000..68f0f638 --- /dev/null +++ b/stdcomponents/render-order.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type RenderOrder struct { + CalculatedZ float32 +} + +type RenderOrderComponentManager = ecs.ComponentManager[RenderOrder] + +func NewRenderOrderComponentManager() RenderOrderComponentManager { + return ecs.NewComponentManager[RenderOrder](RenderOrderComponentId) +} diff --git a/stdcomponents/ysort.go b/stdcomponents/ysort.go new file mode 100644 index 00000000..1eef5b57 --- /dev/null +++ b/stdcomponents/ysort.go @@ -0,0 +1,26 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type YSort struct { +} + +type YSortComponentManager = ecs.ComponentManager[YSort] + +func NewYSortComponentManager() YSortComponentManager { + return ecs.NewComponentManager[YSort](YSortComponentId) +} diff --git a/stdsystems/render.go b/stdsystems/render.go index af3fb5d3..f14ab5a1 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -19,7 +19,8 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/pkg/ecs" "gomp/stdcomponents" - "sort" + "math" + "slices" "sync" "time" ) @@ -40,6 +41,7 @@ type RenderSystem struct { Renderables *stdcomponents.RenderableComponentManager AnimationStates *stdcomponents.AnimationStateComponentManager SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager renderList []RenderEntry instanceData []stdcomponents.RLTexturePro camera rl.Camera2D @@ -91,23 +93,20 @@ func (s *RenderSystem) render() { } s.Renderables.EachEntity(func(e ecs.Entity) bool { sprite := s.SpriteMatrixes.Get(e) - pos := s.Positions.Get(e) + renderOrder := s.RenderOrders.Get(e) s.renderList = append(s.renderList, RenderEntry{ Entity: e, TextureId: int(sprite.Texture.ID), - ZIndex: pos.Z, + ZIndex: renderOrder.CalculatedZ, }) return true }) - // Sort by texture, then Z-index - sort.SliceStable(s.renderList, func(i, j int) bool { - a := s.renderList[i] - b := s.renderList[j] + slices.SortStableFunc(s.renderList, func(a, b RenderEntry) int { if a.TextureId == b.TextureId { - return a.ZIndex < b.ZIndex + return int(math.Floor(float64(a.ZIndex - b.ZIndex))) } - return a.TextureId < b.TextureId + return int(a.TextureId - b.TextureId) }) // Batch and render diff --git a/stdsystems/ysort.go b/stdsystems/ysort.go new file mode 100644 index 00000000..4875b242 --- /dev/null +++ b/stdsystems/ysort.go @@ -0,0 +1,50 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +const ySortOffsetScale float32 = 0.001 + +func NewYSortSystem() YSortSystem { + return YSortSystem{} +} + +type YSortSystem struct { + EntityManager *ecs.EntityManager + YSorts *stdcomponents.YSortComponentManager + Positions *stdcomponents.PositionComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager +} + +func (s *YSortSystem) Init() {} +func (s *YSortSystem) Run() { + s.YSorts.EachEntity(func(entity ecs.Entity) bool { + pos := s.Positions.Get(entity) + renderOrder := s.RenderOrders.Get(entity) + + // Calculate depth based on Y position + yDepth := pos.Y * ySortOffsetScale + + // Preserve original Z layer but add Y-based offset + renderOrder.CalculatedZ = float32(int(pos.Z)) + yDepth + + return true + }) +} +func (s *YSortSystem) Destroy() {} From a58d91f470f13bc55e4eb1513a56241898635d6c Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 5 Mar 2025 18:23:43 +0300 Subject: [PATCH 032/196] feat simple collision system --- examples/new-api/entities/player.go | 15 +++ examples/new-api/instances/component-list.go | 6 + examples/new-api/instances/system-list.go | 2 + examples/new-api/scenes/main-scene.go | 5 + examples/new-api/systems/player.go | 7 +- go.mod | 1 + go.sum | 54 +++++++++ internal/geometry/main.go | 121 +++++++++++++++++++ pkg/ecs/component-manager.go | 4 + pkg/ecs/paged-array.go | 4 +- stdcomponents/colliders.go | 56 +++++++++ stdcomponents/ids.go | 3 + stdsystems/animation-spritematrix.go | 2 - stdsystems/collision.go | 91 ++++++++++++++ 14 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 internal/geometry/main.go create mode 100644 stdcomponents/colliders.go create mode 100644 stdsystems/collision.go diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index a9744ac0..5a4a6972 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -36,6 +36,8 @@ type Player struct { Renderable *stdcomponents.Renderable YSort *stdcomponents.YSort RenderOrder *stdcomponents.RenderOrder + ColliderBox *stdcomponents.ColliderBox + Collision *stdcomponents.Collision } func CreatePlayer( @@ -52,6 +54,8 @@ func CreatePlayer( renderables *stdcomponents.RenderableComponentManager, ySorts *stdcomponents.YSortComponentManager, renderOrders *stdcomponents.RenderOrderComponentManager, + boxColliders *stdcomponents.ColliderBoxComponentManager, + collisions *stdcomponents.CollisionComponentManager, ) (player Player) { // Creating new player @@ -103,5 +107,16 @@ func CreatePlayer( // Adding RenderOrder component player.RenderOrder = renderOrders.Create(entity, stdcomponents.RenderOrder{}) + // Adding ColliderBox component + player.ColliderBox = boxColliders.Create(entity, stdcomponents.ColliderBox{ + Width: 16, + Height: 16, + }) + + // Adding Collision component + player.Collision = collisions.Create(entity, stdcomponents.Collision{ + Shape: stdcomponents.BoxColliderShape, + }) + return player } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index e178443c..abdbc0f5 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -34,6 +34,9 @@ type ComponentList struct { Renderable stdcomponents.RenderableComponentManager YSort stdcomponents.YSortComponentManager RenderOrder stdcomponents.RenderOrderComponentManager + Collision stdcomponents.CollisionComponentManager + ColliderBox stdcomponents.ColliderBoxComponentManager + ColliderCircle stdcomponents.ColliderCircleComponentManager Health components.HealthComponentManager Controller components.ControllerComponentManager @@ -55,6 +58,9 @@ func NewComponentList() ComponentList { Renderable: stdcomponents.NewRenderableComponentManager(), YSort: stdcomponents.NewYSortComponentManager(), RenderOrder: stdcomponents.NewRenderOrderComponentManager(), + Collision: stdcomponents.NewCollisionComponentManager(), + ColliderBox: stdcomponents.NewColliderBoxComponentManager(), + ColliderCircle: stdcomponents.NewColliderCircleComponentManager(), Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index 66af7b96..ba2327c3 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -36,6 +36,7 @@ func NewSystemList() SystemList { SpriteMatrix: stdsystems.NewSpriteMatrixSystem(), AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{assets.Textures}), YSort: stdsystems.NewYSortSystem(), + Collision: stdsystems.NewCollisionSystem(), Render: stdsystems.NewRenderSystem(), } @@ -56,5 +57,6 @@ type SystemList struct { SpriteMatrix stdsystems.SpriteMatrixSystem AssetLib stdsystems.AssetLibSystem YSort stdsystems.YSortSystem + Collision stdsystems.CollisionSystem Render stdsystems.RenderSystem } diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index aa261f99..a26724fb 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -47,6 +47,8 @@ func (s *MainScene) Init() { s.World.Systems.Player.Init() s.World.Systems.Velocity.Init() + s.World.Systems.Collision.Init() + // Network patches s.World.Systems.NetworkSend.Init() @@ -76,6 +78,7 @@ func (s *MainScene) FixedUpdate(dt time.Duration) { s.World.Systems.Network.Run(dt) s.World.Systems.Velocity.Run(dt) + s.World.Systems.Collision.Run(dt) s.World.Systems.NetworkSend.Run(dt) } @@ -104,6 +107,8 @@ func (s *MainScene) Destroy() { s.World.Systems.Player.Destroy() + s.World.Systems.Collision.Destroy() + // Network patches s.World.Systems.NetworkSend.Destroy() diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index a6ae9a18..e19073d9 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -37,20 +37,23 @@ type PlayerSystem struct { Renderables *stdcomponents.RenderableComponentManager YSorts *stdcomponents.YSortComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager + BoxColliders *stdcomponents.ColliderBoxComponentManager + Collisions *stdcomponents.CollisionComponentManager } func (s *PlayerSystem) Init() { s.SpriteMatrixes.Create(sprites.PlayerSpriteSharedComponentId, sprites.PlayerSpriteMatrix) - for range 20_000 { + for range 2_000 { s.Player = entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, - s.YSorts, s.RenderOrders, + s.YSorts, s.RenderOrders, s.BoxColliders, s.Collisions, ) s.Player.Position.X = 100 + rand.Float32()*700 s.Player.Position.Y = 100 + rand.Float32()*500 + s.Player.AnimationPlayer.Current = uint8(rand.Intn(7)) } s.Player.Position.X = 100 diff --git a/go.mod b/go.mod index a5351387..8033b0ca 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/paulmach/orb v0.11.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect diff --git a/go.sum b/go.sum index b0ea89d2..a7f2676c 100644 --- a/go.sum +++ b/go.sum @@ -43,16 +43,21 @@ github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= @@ -90,7 +95,9 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345 h1:t3LCgVzMRS2q7fL4T9pgshIUQuqBQD+ERMKUJNgL+Qo= github.com/jupiterrider/purego-sdl3 v0.0.0-20250223121749-61a56748f345/go.mod h1:bUv1BcdO0uvGAb0mT5PDSInh67TlbzXBBL4/vVwcEEs= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -113,6 +120,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/negrel/assert v0.5.0 h1:woWYcJDBNLMxpIv9XaRacA0l9K6cStkoYygu58J4DzI= @@ -124,7 +132,11 @@ github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3Ro github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= +github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -171,6 +183,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= @@ -179,8 +192,15 @@ github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/ github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/yohamta/donburi v1.15.7 h1:so/vHf1L133d0SFVrCUzMMueh2ko39wRkrcpNLdzvz8= github.com/yohamta/donburi v1.15.7/go.mod h1:FdjU9hpwAsAs1qRvqsSTJimPJ0dipvdnr9hMJXYc1Rk= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= @@ -189,6 +209,9 @@ golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -199,6 +222,8 @@ golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -209,6 +234,11 @@ golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -220,6 +250,10 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -227,13 +261,22 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -241,11 +284,19 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -262,9 +313,12 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= diff --git a/internal/geometry/main.go b/internal/geometry/main.go new file mode 100644 index 00000000..969ca3ad --- /dev/null +++ b/internal/geometry/main.go @@ -0,0 +1,121 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "fmt" +) + +// Point представляет точку с координатами X и Y. +type Point struct { + X, Y float64 +} + +// Polygon представляет полигон как набор точек. +type Polygon []Point + +// Projection представляет проекцию полигона на ось. +type Projection struct { + Min, Max float64 +} + +// Main функция для проверки пересечения двух полигонов. +func PolygonsIntersect(p1, p2 Polygon) bool { + // Получаем все оси для проверки + axes := getAxes(p1) + axes = append(axes, getAxes(p2)...) + + // Проверяем каждую ось + for _, axis := range axes { + // Проецируем оба полигона на ось + proj1 := project(p1, axis) + proj2 := project(p2, axis) + + // Проверяем, перекрываются ли проекции + if !overlap(proj1, proj2) { + return false + } + } + + return true +} + +// getAxes возвращает все нормали к ребрам полигона. +func getAxes(p Polygon) []Point { + axes := make([]Point, 0, len(p)) + for i := 0; i < len(p); i++ { + p1 := p[i] + p2 := p[(i+1)%len(p)] // Следующая точка (замыкаем полигон) + + // Вектор ребра + edge := Point{p2.X - p1.X, p2.Y - p1.Y} + + // Нормаль к ребру (перпендикулярный вектор) + normal := Point{-edge.Y, edge.X} + axes = append(axes, normal) + } + return axes +} + +// project проецирует полигон на ось и возвращает проекцию. +func project(p Polygon, axis Point) Projection { + min := dot(p[0], axis) + max := min + + for _, point := range p { + proj := dot(point, axis) + if proj < min { + min = proj + } + if proj > max { + max = proj + } + } + + return Projection{Min: min, Max: max} +} + +// dot возвращает скалярное произведение двух точек. +func dot(p1, p2 Point) float64 { + return p1.X*p2.X + p1.Y*p2.Y +} + +// overlap проверяет, перекрываются ли две проекции. +func overlap(proj1, proj2 Projection) bool { + return proj1.Min <= proj2.Max && proj2.Min <= proj1.Max +} + +func main() { + // Пример двух полигонов (выпуклых) + polygon1 := Polygon{ + {0, 0}, + {4, 0}, + {4, 4}, + {0, 4}, + } + polygon2 := Polygon{ + {2, 2}, + {6, 2}, + {6, 6}, + {2, 6}, + } + + // Проверяем пересечение + if PolygonsIntersect(polygon1, polygon2) { + fmt.Println("Полигоны пересекаются.") + } else { + fmt.Println("Полигоны не пересекаются.") + } +} diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 39583fb0..28eaca02 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -354,6 +354,10 @@ func (c *ComponentManager[T]) RawComponents(ptr []T) { c.components.Raw(ptr) } +func (c *ComponentManager[T]) RawEntities(ptr []Entity) []Entity { + return c.entities.Raw(ptr) +} + func (c *ComponentManager[T]) assertBegin() { assert.True(c.isInitialized, "ComponentManager should be created with NewComponentManager()") assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 447c2f06..654766ec 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -137,12 +137,14 @@ func (a *PagedArray[T]) Last() *T { return a.Get(index) } -func (a *PagedArray[T]) Raw(result []T) { +func (a *PagedArray[T]) Raw(result []T) []T { result = result[:0] for i := 0; i <= a.currentPageIndex; i++ { page := &a.book[i] result = append(result[:i*1024], append(result[i*1024:], page.data[:page.len]...)...) } + + return result } func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go new file mode 100644 index 00000000..6e2c9b1b --- /dev/null +++ b/stdcomponents/colliders.go @@ -0,0 +1,56 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type ColliderShape = uint8 + +const ( + InvalidColliderShape ColliderShape = iota + BoxColliderShape + CircleColliderShape +) + +type ColliderBox struct { + Width float32 + Height float32 +} + +type ColliderBoxComponentManager = ecs.ComponentManager[ColliderBox] + +func NewColliderBoxComponentManager() ColliderBoxComponentManager { + return ecs.NewComponentManager[ColliderBox](ColliderBoxComponentId) +} + +type ColliderCircle struct { + Radius float32 +} + +type ColliderCircleComponentManager = ecs.ComponentManager[ColliderCircle] + +func NewColliderCircleComponentManager() ColliderCircleComponentManager { + return ecs.NewComponentManager[ColliderCircle](ColliderCircleComponentId) +} + +type Collision struct { + Shape ColliderShape +} + +type CollisionComponentManager = ecs.ComponentManager[Collision] + +func NewCollisionComponentManager() CollisionComponentManager { + return ecs.NewComponentManager[Collision](CollisionComponentId) +} diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 6bdc5e33..1620c3c4 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -28,5 +28,8 @@ const ( RenderableComponentId YSortComponentId RenderOrderComponentId + CollisionComponentId + ColliderBoxComponentId + ColliderCircleComponentId StdComponentIds ) diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index cc5612b8..2a07ec7f 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -44,8 +44,6 @@ func (s *AnimationSpriteMatrixSystem) Run() { currentAnimation := spriteMatrix.Animations[animationState] - animationPlayer.First = 0 - animationPlayer.Current = 0 animationPlayer.Last = currentAnimation.NumOfFrames - 1 animationPlayer.Loop = currentAnimation.Loop animationPlayer.Vertical = currentAnimation.Vertical diff --git a/stdsystems/collision.go b/stdsystems/collision.go new file mode 100644 index 00000000..a1a548ab --- /dev/null +++ b/stdsystems/collision.go @@ -0,0 +1,91 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "sync" + "time" +) + +func NewCollisionSystem() CollisionSystem { + return CollisionSystem{} +} + +type CollisionSystem struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Collisions *stdcomponents.CollisionComponentManager + BoxColliders *stdcomponents.ColliderBoxComponentManager +} + +func (s *CollisionSystem) Init() {} +func (s *CollisionSystem) Run(dt time.Duration) { + var entities []ecs.Entity = make([]ecs.Entity, 0, s.Collisions.Len()) + entities = s.Collisions.RawEntities(entities) + + wg := &sync.WaitGroup{} + wg.Add(len(entities)) + + // Simple AABB collision detection + for i := 0; i < len(entities); i++ { + collision1 := s.Collisions.Get(entities[i]) + + switch collision1.Shape { + case stdcomponents.BoxColliderShape: + go s.boxToXCollision(entities, i, wg) + case stdcomponents.CircleColliderShape: + s.circleToXCollision(entities, i) + default: + panic("Unknown collision shape") + } + } + + wg.Wait() +} +func (s *CollisionSystem) Destroy() {} +func (s *CollisionSystem) boxToXCollision(entities []ecs.Entity, i int, wg *sync.WaitGroup) { + defer wg.Done() + + position1 := s.Positions.Get(entities[i]) + collider1 := s.BoxColliders.Get(entities[i]) + + for j := i + 1; j < len(entities); j++ { + position2 := s.Positions.Get(entities[j]) + collision2 := s.Collisions.Get(entities[j]) + collider2 := s.BoxColliders.Get(entities[j]) + + switch collision2.Shape { + case stdcomponents.BoxColliderShape: + // Check AABB collision + if !(position1.X+collider1.Width < position2.X || + position1.X > position2.X+collider2.Width || + position1.Y+collider1.Height < position2.Y || + position1.Y > position2.Y+collider2.Height) { + // Handle collision + //fmt.Printf("Collision between entities %d and %d\n", entities[i], entities[j]) + } + case stdcomponents.CircleColliderShape: + panic("Circle-Box collision not implemented") + default: + panic("Unknown collision shape") + } + } +} + +func (s *CollisionSystem) circleToXCollision(entities []ecs.Entity, i int) { + panic("Circle-X collision not implemented") +} From 891a7e5cfb42f7dec927d67123079adad4123513 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 6 Mar 2025 15:20:51 +0300 Subject: [PATCH 033/196] feat collision events --- examples/new-api/entities/player.go | 8 +- examples/new-api/instances/component-list.go | 6 +- examples/new-api/systems/player.go | 4 +- stdcomponents/colliders.go | 8 +- stdcomponents/collision.go | 38 ++++++ stdcomponents/ids.go | 3 +- stdsystems/collision.go | 136 ++++++++++++++----- 7 files changed, 159 insertions(+), 44 deletions(-) create mode 100644 stdcomponents/collision.go diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index 5a4a6972..24bd253e 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -37,7 +37,7 @@ type Player struct { YSort *stdcomponents.YSort RenderOrder *stdcomponents.RenderOrder ColliderBox *stdcomponents.ColliderBox - Collision *stdcomponents.Collision + GenericCollider *stdcomponents.GenericCollider } func CreatePlayer( @@ -55,7 +55,7 @@ func CreatePlayer( ySorts *stdcomponents.YSortComponentManager, renderOrders *stdcomponents.RenderOrderComponentManager, boxColliders *stdcomponents.ColliderBoxComponentManager, - collisions *stdcomponents.CollisionComponentManager, + genericColliders *stdcomponents.GenericColliderComponentManager, ) (player Player) { // Creating new player @@ -113,8 +113,8 @@ func CreatePlayer( Height: 16, }) - // Adding Collision component - player.Collision = collisions.Create(entity, stdcomponents.Collision{ + // Adding GenericCollider component + player.GenericCollider = genericColliders.Create(entity, stdcomponents.GenericCollider{ Shape: stdcomponents.BoxColliderShape, }) diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index abdbc0f5..c08261e3 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -34,9 +34,10 @@ type ComponentList struct { Renderable stdcomponents.RenderableComponentManager YSort stdcomponents.YSortComponentManager RenderOrder stdcomponents.RenderOrderComponentManager - Collision stdcomponents.CollisionComponentManager + GenericCollider stdcomponents.GenericColliderComponentManager ColliderBox stdcomponents.ColliderBoxComponentManager ColliderCircle stdcomponents.ColliderCircleComponentManager + Collision stdcomponents.CollisionComponentManager Health components.HealthComponentManager Controller components.ControllerComponentManager @@ -58,9 +59,10 @@ func NewComponentList() ComponentList { Renderable: stdcomponents.NewRenderableComponentManager(), YSort: stdcomponents.NewYSortComponentManager(), RenderOrder: stdcomponents.NewRenderOrderComponentManager(), - Collision: stdcomponents.NewCollisionComponentManager(), + GenericCollider: stdcomponents.NewGenericColliderComponentManager(), ColliderBox: stdcomponents.NewColliderBoxComponentManager(), ColliderCircle: stdcomponents.NewColliderCircleComponentManager(), + Collision: stdcomponents.NewCollisionComponentManager(), Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index e19073d9..cb3d11b6 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -38,7 +38,7 @@ type PlayerSystem struct { YSorts *stdcomponents.YSortComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager BoxColliders *stdcomponents.ColliderBoxComponentManager - Collisions *stdcomponents.CollisionComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager } func (s *PlayerSystem) Init() { @@ -48,7 +48,7 @@ func (s *PlayerSystem) Init() { s.Player = entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, - s.YSorts, s.RenderOrders, s.BoxColliders, s.Collisions, + s.YSorts, s.RenderOrders, s.BoxColliders, s.GenericCollider, ) s.Player.Position.X = 100 + rand.Float32()*700 diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 6e2c9b1b..c291ed38 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -45,12 +45,12 @@ func NewColliderCircleComponentManager() ColliderCircleComponentManager { return ecs.NewComponentManager[ColliderCircle](ColliderCircleComponentId) } -type Collision struct { +type GenericCollider struct { Shape ColliderShape } -type CollisionComponentManager = ecs.ComponentManager[Collision] +type GenericColliderComponentManager = ecs.ComponentManager[GenericCollider] -func NewCollisionComponentManager() CollisionComponentManager { - return ecs.NewComponentManager[Collision](CollisionComponentId) +func NewGenericColliderComponentManager() GenericColliderComponentManager { + return ecs.NewComponentManager[GenericCollider](GenericColliderComponentId) } diff --git a/stdcomponents/collision.go b/stdcomponents/collision.go new file mode 100644 index 00000000..81a79ab7 --- /dev/null +++ b/stdcomponents/collision.go @@ -0,0 +1,38 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type CollisionState uint8 + +const ( + CollisionStateNone CollisionState = iota + CollisionStateEnter + CollisionStateStay + CollisionStateExit +) + +// Collision Marks a proxy entity as representing a collision pair between E1 and E2 +type Collision struct { + E1, E2 ecs.Entity + State CollisionState +} + +type CollisionComponentManager = ecs.ComponentManager[Collision] + +func NewCollisionComponentManager() CollisionComponentManager { + return ecs.NewComponentManager[Collision](CollisionComponentId) +} diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 1620c3c4..e1197c1d 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -28,8 +28,9 @@ const ( RenderableComponentId YSortComponentId RenderOrderComponentId - CollisionComponentId + GenericColliderComponentId ColliderBoxComponentId ColliderCircleComponentId + CollisionComponentId StdComponentIds ) diff --git a/stdsystems/collision.go b/stdsystems/collision.go index a1a548ab..d6799391 100644 --- a/stdsystems/collision.go +++ b/stdsystems/collision.go @@ -26,57 +26,131 @@ func NewCollisionSystem() CollisionSystem { } type CollisionSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Collisions *stdcomponents.CollisionComponentManager - BoxColliders *stdcomponents.ColliderBoxComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.ColliderBoxComponentManager + Collisions *stdcomponents.CollisionComponentManager + + activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities + currentCollisions map[CollisionPair]struct{} + currentCollisionsMx sync.RWMutex + activeCollisionsMx sync.Mutex } -func (s *CollisionSystem) Init() {} +type CollisionPair struct { + E1, E2 ecs.Entity +} + +func (c CollisionPair) Normalize() CollisionPair { + if c.E1 > c.E2 { + return CollisionPair{c.E2, c.E1} + } + return c +} + +func (s *CollisionSystem) Init() { + s.activeCollisions = make(map[CollisionPair]ecs.Entity) +} func (s *CollisionSystem) Run(dt time.Duration) { - var entities []ecs.Entity = make([]ecs.Entity, 0, s.Collisions.Len()) - entities = s.Collisions.RawEntities(entities) + s.currentCollisions = make(map[CollisionPair]struct{}) - wg := &sync.WaitGroup{} - wg.Add(len(entities)) + entities := s.GenericCollider.RawEntities(make([]ecs.Entity, 0, s.GenericCollider.Len())) - // Simple AABB collision detection - for i := 0; i < len(entities); i++ { - collision1 := s.Collisions.Get(entities[i]) + const batchSize = 64 // Tune this based on performance testing + wg := new(sync.WaitGroup) - switch collision1.Shape { - case stdcomponents.BoxColliderShape: - go s.boxToXCollision(entities, i, wg) - case stdcomponents.CircleColliderShape: - s.circleToXCollision(entities, i) - default: - panic("Unknown collision shape") + // Process entities in batches + for start := 0; start < len(entities); start += batchSize { + end := start + batchSize + if end > len(entities) { + end = len(entities) } + + wg.Add(1) + go func(start, end int) { + defer wg.Done() + for i := start; i < end; i++ { + entity := entities[i] + collision := s.GenericCollider.Get(entity) + + switch collision.Shape { + case stdcomponents.BoxColliderShape: + s.boxToXCollision(entities, i) + case stdcomponents.CircleColliderShape: + s.circleToXCollision(entities, i) + default: + panic("Unknown collision shape") + } + } + }(start, end) } wg.Wait() + + s.processExitStates() } func (s *CollisionSystem) Destroy() {} -func (s *CollisionSystem) boxToXCollision(entities []ecs.Entity, i int, wg *sync.WaitGroup) { - defer wg.Done() - position1 := s.Positions.Get(entities[i]) - collider1 := s.BoxColliders.Get(entities[i]) +func (s *CollisionSystem) registerCollision(entityA, entityB ecs.Entity) { + pair := CollisionPair{entityA, entityB}.Normalize() + + s.currentCollisionsMx.Lock() + s.currentCollisions[pair] = struct{}{} + s.currentCollisionsMx.Unlock() + + s.activeCollisionsMx.Lock() + defer s.activeCollisionsMx.Unlock() + + // Create proxy entity for new collisions + if _, exists := s.activeCollisions[pair]; !exists { + proxy := s.EntityManager.Create() + s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) + s.activeCollisions[pair] = proxy + } else { + s.Collisions.Get(s.activeCollisions[pair]).State = stdcomponents.CollisionStateStay + } +} + +func (s *CollisionSystem) processExitStates() { + s.activeCollisionsMx.Lock() + defer s.activeCollisionsMx.Unlock() + s.currentCollisionsMx.RLock() + defer s.currentCollisionsMx.RUnlock() + + for pair, proxy := range s.activeCollisions { + if _, exists := s.currentCollisions[pair]; !exists { + collision := s.Collisions.Get(proxy) + if collision.State == stdcomponents.CollisionStateExit { + delete(s.activeCollisions, pair) + s.EntityManager.Delete(proxy) + } else { + collision.State = stdcomponents.CollisionStateExit + } + } + } +} + +func (s *CollisionSystem) boxToXCollision(entities []ecs.Entity, i int) { + entityA := entities[i] + position1 := s.Positions.Get(entityA) + collider1 := s.BoxColliders.Get(entityA) for j := i + 1; j < len(entities); j++ { - position2 := s.Positions.Get(entities[j]) - collision2 := s.Collisions.Get(entities[j]) - collider2 := s.BoxColliders.Get(entities[j]) + entityB := entities[j] + position2 := s.Positions.Get(entityB) + genericCollider := s.GenericCollider.Get(entityB) + boxCollider := s.BoxColliders.Get(entityB) - switch collision2.Shape { + switch genericCollider.Shape { case stdcomponents.BoxColliderShape: // Check AABB collision if !(position1.X+collider1.Width < position2.X || - position1.X > position2.X+collider2.Width || + position1.X > position2.X+boxCollider.Width || position1.Y+collider1.Height < position2.Y || - position1.Y > position2.Y+collider2.Height) { - // Handle collision - //fmt.Printf("Collision between entities %d and %d\n", entities[i], entities[j]) + position1.Y > position2.Y+boxCollider.Height) { + + s.registerCollision(entityA, entityB) } case stdcomponents.CircleColliderShape: panic("Circle-Box collision not implemented") From 640b2c591d2aa781be29db862dcfbcfb85ba4bb6 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 7 Mar 2025 00:17:27 +0300 Subject: [PATCH 034/196] feat spatial collision and grid --- examples/new-api/components/ids.go | 1 + examples/new-api/components/tags.go | 26 +++ examples/new-api/entities/player.go | 4 +- examples/new-api/instances/component-list.go | 2 + examples/new-api/instances/system-list.go | 4 + examples/new-api/scenes/main-scene.go | 3 +- examples/new-api/systems/collision-handler.go | 60 +++++ examples/new-api/systems/player.go | 49 +++-- pkg/ecs/component-manager-shared.go | 25 ++- pkg/ecs/component-manager.go | 2 +- pkg/ecs/entity-manager.go | 4 + pkg/ecs/grid.go | 144 ++++++++++-- stdsystems/collision-spatial.go | 208 ++++++++++++++++++ stdsystems/render.go | 16 ++ 14 files changed, 499 insertions(+), 49 deletions(-) create mode 100644 examples/new-api/components/tags.go create mode 100644 examples/new-api/systems/collision-handler.go create mode 100644 stdsystems/collision-spatial.go diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go index 774ca3a2..00fd9c3c 100644 --- a/examples/new-api/components/ids.go +++ b/examples/new-api/components/ids.go @@ -21,4 +21,5 @@ import ( const ( HealthComponentId = iota + stdcomponents.StdComponentIds ControllerComponentId + PlayerTagComponentId ) diff --git a/examples/new-api/components/tags.go b/examples/new-api/components/tags.go new file mode 100644 index 00000000..f59165a3 --- /dev/null +++ b/examples/new-api/components/tags.go @@ -0,0 +1,26 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +type PlayerTag struct { +} + +type PlayerTagComponentManager = ecs.ComponentManager[PlayerTag] + +func NewPlayerTagComponentManager() PlayerTagComponentManager { + return ecs.NewComponentManager[PlayerTag](PlayerTagComponentId) +} diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index 24bd253e..d1fe7150 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -109,8 +109,8 @@ func CreatePlayer( // Adding ColliderBox component player.ColliderBox = boxColliders.Create(entity, stdcomponents.ColliderBox{ - Width: 16, - Height: 16, + Width: 96, + Height: 128, }) // Adding GenericCollider component diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index c08261e3..7046646d 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -41,6 +41,7 @@ type ComponentList struct { Health components.HealthComponentManager Controller components.ControllerComponentManager + PlayerTag components.PlayerTagComponentManager } func NewComponentList() ComponentList { @@ -66,5 +67,6 @@ func NewComponentList() ComponentList { Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), + PlayerTag: components.NewPlayerTagComponentManager(), } } diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index ba2327c3..6d75f4a8 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -37,6 +37,8 @@ func NewSystemList() SystemList { AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{assets.Textures}), YSort: stdsystems.NewYSortSystem(), Collision: stdsystems.NewCollisionSystem(), + SpatialCollision: stdsystems.NewSpatialCollisionSystem(), + CollisionHandler: systems.NewCollisionHandlerSystem(), Render: stdsystems.NewRenderSystem(), } @@ -58,5 +60,7 @@ type SystemList struct { AssetLib stdsystems.AssetLibSystem YSort stdsystems.YSortSystem Collision stdsystems.CollisionSystem + CollisionHandler systems.CollisionHandlerSystem + SpatialCollision stdsystems.SpatialCollisionSystem Render stdsystems.RenderSystem } diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index a26724fb..86878ee3 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -78,7 +78,8 @@ func (s *MainScene) FixedUpdate(dt time.Duration) { s.World.Systems.Network.Run(dt) s.World.Systems.Velocity.Run(dt) - s.World.Systems.Collision.Run(dt) + s.World.Systems.SpatialCollision.Run(dt) + s.World.Systems.CollisionHandler.Run(dt) s.World.Systems.NetworkSend.Run(dt) } diff --git a/examples/new-api/systems/collision-handler.go b/examples/new-api/systems/collision-handler.go new file mode 100644 index 00000000..a51783a0 --- /dev/null +++ b/examples/new-api/systems/collision-handler.go @@ -0,0 +1,60 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package systems + +import ( + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "log" + "time" +) + +func NewCollisionHandlerSystem() CollisionHandlerSystem { + return CollisionHandlerSystem{} +} + +type CollisionHandlerSystem struct { + EntityManager *ecs.EntityManager + Collisions *stdcomponents.CollisionComponentManager + Players *components.PlayerTagComponentManager +} + +func (s *CollisionHandlerSystem) Init() {} +func (s *CollisionHandlerSystem) Run(dt time.Duration) { + s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { + if collision.State == stdcomponents.CollisionStateEnter { + log.Println("Collision entered") + e1Tag := s.Players.Get(collision.E1) + e2Tag := s.Players.Get(collision.E2) + + if e1Tag != nil { + log.Println("PlayerTag 1 collided with player 2") + s.EntityManager.Delete(collision.E2) + } + if e2Tag != nil { + log.Println("PlayerTag 2 collided with player 1") + s.EntityManager.Delete(collision.E1) + } + } + + if collision.State == stdcomponents.CollisionStateExit { + log.Println("Collision exited") + } + + return true + }) +} +func (s *CollisionHandlerSystem) Destroy() {} diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index cb3d11b6..daa847dd 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -22,7 +22,6 @@ func NewPlayerSystem() PlayerSystem { type PlayerSystem struct { EntityManager *ecs.EntityManager - Player entities.Player SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager Positions *stdcomponents.PositionComponentManager Rotations *stdcomponents.RotationComponentManager @@ -39,64 +38,74 @@ type PlayerSystem struct { RenderOrders *stdcomponents.RenderOrderComponentManager BoxColliders *stdcomponents.ColliderBoxComponentManager GenericCollider *stdcomponents.GenericColliderComponentManager + Players *components.PlayerTagComponentManager } func (s *PlayerSystem) Init() { s.SpriteMatrixes.Create(sprites.PlayerSpriteSharedComponentId, sprites.PlayerSpriteMatrix) - for range 2_000 { - s.Player = entities.CreatePlayer( + for range 50 { + npc := entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, s.YSorts, s.RenderOrders, s.BoxColliders, s.GenericCollider, ) - s.Player.Position.X = 100 + rand.Float32()*700 - s.Player.Position.Y = 100 + rand.Float32()*500 - s.Player.AnimationPlayer.Current = uint8(rand.Intn(7)) + npc.Position.X = 100 + rand.Float32()*700 + npc.Position.Y = 100 + rand.Float32()*500 + npc.AnimationPlayer.Current = uint8(rand.Intn(7)) } - s.Player.Position.X = 100 - s.Player.Position.Y = 100 + player := entities.CreatePlayer( + s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, + s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, + s.YSorts, s.RenderOrders, s.BoxColliders, s.GenericCollider, + ) + player.Position.X = 100 + player.Position.Y = 100 - s.Controllers.Create(s.Player.Entity, components.Controller{}) + s.Controllers.Create(player.Entity, components.Controller{}) + s.Players.Create(player.Entity, components.PlayerTag{}) } func (s *PlayerSystem) Run() { - animationState := s.AnimationStates.Get(s.Player.Entity) var speed float32 = 300 - s.Player.Velocity.X = 0 - s.Player.Velocity.Y = 0 + for e := range s.Controllers.EachEntity { + velocity := s.Velocities.Get(e) + flip := s.Flips.Get(e) + animationState := s.AnimationStates.Get(e) + + velocity.X = 0 + velocity.Y = 0 - for range s.Controllers.EachComponent { if rl.IsKeyDown(rl.KeySpace) { *animationState = entities.PlayerStateJump } else { *animationState = entities.PlayerStateIdle if rl.IsKeyDown(rl.KeyD) { *animationState = entities.PlayerStateWalk - s.Player.Velocity.X = speed - s.Player.Flip.X = false + velocity.X = speed + flip.X = false } if rl.IsKeyDown(rl.KeyA) { *animationState = entities.PlayerStateWalk - s.Player.Velocity.X = -speed - s.Player.Flip.X = true + velocity.X = -speed + flip.X = true } if rl.IsKeyDown(rl.KeyW) { *animationState = entities.PlayerStateWalk - s.Player.Velocity.Y = -speed + velocity.Y = -speed } if rl.IsKeyDown(rl.KeyS) { *animationState = entities.PlayerStateWalk - s.Player.Velocity.Y = speed + velocity.Y = speed } } if rl.IsKeyPressed(rl.KeyK) { - s.EntityManager.Delete(s.Player.Entity) + s.EntityManager.Delete(e) } } diff --git a/pkg/ecs/component-manager-shared.go b/pkg/ecs/component-manager-shared.go index 34483a4c..c5666b54 100644 --- a/pkg/ecs/component-manager-shared.go +++ b/pkg/ecs/component-manager-shared.go @@ -14,6 +14,8 @@ import ( type SharedComponentInstanceId uint16 +var _ AnyComponentManagerPtr = &SharedComponentManager[any]{} + func NewSharedComponentManager[T any](id ComponentId) SharedComponentManager[T] { newManager := SharedComponentManager[T]{ components: NewPagedArray[T](), @@ -65,6 +67,26 @@ type SharedComponentManager[T any] struct { decoder func([]byte) []T } +func (c *SharedComponentManager[T]) PatchAdd(entity Entity) { + //TODO implement me + panic("implement me") +} + +func (c *SharedComponentManager[T]) PatchGet() ComponentPatch { + //TODO implement me + panic("implement me") +} + +func (c *SharedComponentManager[T]) PatchApply(patch ComponentPatch) { + //TODO implement me + panic("implement me") +} + +func (c *SharedComponentManager[T]) PatchReset() { + //TODO implement me + panic("implement me") +} + func (c *SharedComponentManager[T]) Id() ComponentId { return c.id } @@ -138,6 +160,7 @@ func (c *SharedComponentManager[T]) Set(entity Entity, instanceId SharedComponen componentIndex, _ := c.instanceToComponent.Get(instanceId) c.entityToComponent.Set(entity, componentIndex) c.patchedEntities.Append(entity) + c.entityComponentBitSet.Set(entity, c.id) return c.components.Get(componentIndex) } @@ -185,7 +208,7 @@ func (c *SharedComponentManager[T]) Len() int { func (c *SharedComponentManager[T]) Clean() { // c.entityComponentBitSet.Clean() //c.components.Clean() - // c.entities.Clean() + // c.Entities.Clean() } // ======================================================== diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 28eaca02..9515e3a6 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -196,7 +196,7 @@ func (c *ComponentManager[T]) Len() int { func (c *ComponentManager[T]) Clean() { // c.entityComponentBitSet.Clean() //c.components.Clean() - // c.entities.Clean() + // c.Entities.Clean() } // ======================================================== diff --git a/pkg/ecs/entity-manager.go b/pkg/ecs/entity-manager.go index 6abf095a..4dcdff87 100644 --- a/pkg/ecs/entity-manager.go +++ b/pkg/ecs/entity-manager.go @@ -72,6 +72,8 @@ type EntityManager struct { type Patch []ComponentPatch func (e *EntityManager) Create() Entity { + e.mx.Lock() + defer e.mx.Unlock() var newId = e.generateEntityID() e.size++ @@ -80,6 +82,8 @@ func (e *EntityManager) Create() Entity { } func (e *EntityManager) Delete(entity Entity) { + e.mx.Lock() + defer e.mx.Unlock() e.componentBitSet.AllSet(entity, func(id ComponentId) bool { e.components[id].Remove(entity) return true diff --git a/pkg/ecs/grid.go b/pkg/ecs/grid.go index 65fd0669..4fcb2735 100644 --- a/pkg/ecs/grid.go +++ b/pkg/ecs/grid.go @@ -14,16 +14,33 @@ Thank you for your support! package ecs -import "math" +import ( + "math" + "sync" +) // GridKey is {cellX, cellY} key for SpatialGrid type GridKey = [2]int type GridCell struct { - entities []Entity + Entities []Entity lookup map[Entity]int + Key GridKey } +/* +SpatialGrid + +# To maximize performance: + +- Set cellSize to ~2x average collision radius + +- Call Compact() during level transitions + +- Use GetNearbyCells() for broadphase collision + +- Pair with spatial hashing for very large worlds +*/ type SpatialGrid struct { cells []GridCell keys []GridKey @@ -31,6 +48,7 @@ type SpatialGrid struct { lookupByEntity map[Entity]int cellSize float64 + mx sync.RWMutex updated map[GridKey]struct{} } @@ -46,17 +64,22 @@ func NewSpatialGrid(cellSize float64) SpatialGrid { } // GetCell returns the grid cell for a given position. -func (g *SpatialGrid) GetCell(positionX, positionY float64) (*GridCell, GridKey) { +func (g *SpatialGrid) GetCell(positionX, positionY float64) (*GridCell, GridKey, bool) { cellX := int(math.Floor(positionX / g.cellSize)) cellY := int(math.Floor(positionY / g.cellSize)) - key := GridKey{cellX, cellY} - cell := &g.cells[g.lookupByKey[key]] - return cell, key + cellIndex, exists := g.lookupByKey[key] + if !exists { + return nil, key, false + } + return &g.cells[cellIndex], key, true } // AddEntity adds an entity to the grid. func (g *SpatialGrid) AddEntity(entity Entity, positionX, positionY float64) { + g.mx.Lock() + defer g.mx.Unlock() + cellX := int(math.Floor(positionX / g.cellSize)) cellY := int(math.Floor(positionY / g.cellSize)) @@ -66,27 +89,36 @@ func (g *SpatialGrid) AddEntity(entity Entity, positionX, positionY float64) { if !ok { cellIndex = len(g.cells) g.cells = append(g.cells, GridCell{ - entities: make([]Entity, 0, PREALLOC_DEFAULT), + Entities: make([]Entity, 0, PREALLOC_DEFAULT), lookup: make(map[Entity]int, PREALLOC_DEFAULT), + Key: key, }) } cell := &g.cells[cellIndex] - cell.entities = append(cell.entities, entity) - cell.lookup[entity] = len(cell.entities) - 1 + cell.Entities = append(cell.Entities, entity) + cell.lookup[entity] = len(cell.Entities) - 1 g.lookupByEntity[entity] = cellIndex g.lookupByKey[key] = cellIndex } -// GetEntities returns the entities in a given cell. +// GetEntities returns the Entities in a given cell. func (g *SpatialGrid) GetEntities(cellX, cellY int) []Entity { key := GridKey{cellX, cellY} cellIndex, ok := g.lookupByKey[key] if !ok { return nil } - return g.cells[cellIndex].entities + return g.cells[cellIndex].Entities +} + +func (g *SpatialGrid) GetCellByKey(key GridKey) (*GridCell, GridKey, bool) { + cellIndex, ok := g.lookupByKey[key] + if !ok { + return nil, key, false + } + return &g.cells[cellIndex], key, true } // UpdateEntity updates state of an entity in the grid. @@ -96,23 +128,29 @@ func (g *SpatialGrid) UpdateEntity(entity Entity) { // RemoveEntity removes an entity from the grid. func (g *SpatialGrid) RemoveEntity(entity Entity) { - cellIndex := g.lookupByEntity[entity] + cellIndex, exists := g.lookupByEntity[entity] + if !exists { + return + } + cell := &g.cells[cellIndex] - last := len(cell.entities) - 1 + last := len(cell.Entities) - 1 entityIndex := cell.lookup[entity] if entityIndex != last { - cell.entities[entityIndex] = cell.entities[last] - cell.lookup[cell.entities[last]] = entityIndex + cell.Entities[entityIndex] = cell.Entities[last] + cell.lookup[cell.Entities[last]] = entityIndex } - cell.entities = cell.entities[:last] + cell.Entities = cell.Entities[:last] delete(cell.lookup, entity) delete(g.lookupByEntity, entity) - if len(cell.entities) == 0 { + if len(cell.Entities) == 0 { delete(g.lookupByKey, GridKey{cellIndex, cellIndex}) + g.cells[cellIndex] = GridCell{} // Release memory + g.keys[cellIndex] = GridKey{} } } @@ -132,13 +170,13 @@ func (g *SpatialGrid) MoveEntity(entity Entity, positionX, positionY float64) { } // Remove from old cell - last := len(oldCell.entities) - 1 + last := len(oldCell.Entities) - 1 entityIndex := oldCell.lookup[entity] if entityIndex != last { - oldCell.entities[entityIndex] = oldCell.entities[last] - oldCell.lookup[oldCell.entities[last]] = entityIndex + oldCell.Entities[entityIndex] = oldCell.Entities[last] + oldCell.lookup[oldCell.Entities[last]] = entityIndex } - oldCell.entities = oldCell.entities[:last] + oldCell.Entities = oldCell.Entities[:last] delete(oldCell.lookup, entity) // Add to new cell @@ -146,17 +184,75 @@ func (g *SpatialGrid) MoveEntity(entity Entity, positionX, positionY float64) { if !ok { newCellIndex = len(g.cells) g.cells = append(g.cells, GridCell{ - entities: make([]Entity, 0, PREALLOC_DEFAULT), + Entities: make([]Entity, 0, PREALLOC_DEFAULT), lookup: make(map[Entity]int, PREALLOC_DEFAULT), }) g.lookupByKey[newKey] = newCellIndex g.keys = append(g.keys, newKey) } newCell := &g.cells[newCellIndex] - newCell.entities = append(newCell.entities, entity) - newCell.lookup[entity] = len(newCell.entities) - 1 + newCell.Entities = append(newCell.Entities, entity) + newCell.lookup[entity] = len(newCell.Entities) - 1 // Update entity lookup g.lookupByEntity[entity] = newCellIndex g.updated[newKey] = struct{}{} } + +func (g *SpatialGrid) GetNearbyCells(key GridKey) []GridKey { + return []GridKey{ + {key[0] - 1, key[1] - 1}, {key[0], key[1] - 1}, {key[0] + 1, key[1] - 1}, + {key[0] - 1, key[1]}, key, {key[0] + 1, key[1]}, + {key[0] - 1, key[1] + 1}, {key[0], key[1] + 1}, {key[0] + 1, key[1] + 1}, + } +} + +func (g *SpatialGrid) GetNearbyEntities(key GridKey) []Entity { + entities := make([]Entity, 0, 32) + for _, k := range g.GetNearbyCells(key) { + if cell, _, exists := g.GetCellByKey(k); exists { + entities = append(entities, cell.Entities...) + } + } + return entities +} + +func (g *SpatialGrid) Compact() { + g.mx.Lock() + defer g.mx.Unlock() + + newCells := make([]GridCell, 0, len(g.cells)) + newKeys := make([]GridKey, 0, len(g.keys)) + newLookup := make(map[GridKey]int, len(g.lookupByKey)) + + for i, cell := range g.cells { + if len(cell.Entities) > 0 { + newLookup[g.keys[i]] = len(newCells) + newCells = append(newCells, cell) + newKeys = append(newKeys, g.keys[i]) + } + } + + // Rebuild entity lookup + g.lookupByEntity = make(map[Entity]int, len(g.lookupByEntity)) + for i, cell := range newCells { + for _, e := range cell.Entities { + g.lookupByEntity[e] = i + } + } + + g.cells = newCells + g.keys = newKeys + g.lookupByKey = newLookup +} + +func (g *SpatialGrid) BoundingBoxesIntersect(x1, y1, x2, y2 float64) bool { + // Fast AABB check using spatial grid cell size + dx := math.Abs(x1 - x2) + dy := math.Abs(y1 - y2) + return dx < g.cellSize*2 && dy < g.cellSize*2 +} + +func (g *SpatialGrid) GetActiveCells() []GridCell { + return g.cells +} diff --git a/stdsystems/collision-spatial.go b/stdsystems/collision-spatial.go new file mode 100644 index 00000000..727898de --- /dev/null +++ b/stdsystems/collision-spatial.go @@ -0,0 +1,208 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "runtime" + "sync" + "time" +) + +func NewSpatialCollisionSystem() SpatialCollisionSystem { + return SpatialCollisionSystem{ + activeCollisions: make(map[CollisionPair]ecs.Entity), + } +} + +type SpatialCollisionSystem struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.ColliderBoxComponentManager + Collisions *stdcomponents.CollisionComponentManager + + activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities + currentCollisions map[CollisionPair]struct{} + currentCollisionsMx sync.RWMutex + activeCollisionsMx sync.Mutex + + spatialGrid ecs.SpatialGrid +} + +func (s *SpatialCollisionSystem) Init() {} +func (s *SpatialCollisionSystem) Run(dt time.Duration) { + s.currentCollisions = make(map[CollisionPair]struct{}) + s.spatialGrid = ecs.NewSpatialGrid(256) + + s.Positions.EachEntity(func(entity ecs.Entity) bool { + position := s.Positions.Get(entity) + s.spatialGrid.AddEntity(entity, float64(position.X), float64(position.Y)) + return true + }) + + // 1. Get all non-empty cells from spatial grid + cells := s.spatialGrid.GetActiveCells() + + // 2. Process cells in parallel batches + wg := new(sync.WaitGroup) + wg.Add(len(cells)) + + cellChan := make(chan *ecs.GridCell, len(cells)) + for i := range cells { + cellChan <- &cells[i] + } + defer close(cellChan) + + // Worker pool pattern + for i := 0; i < runtime.NumCPU(); i++ { + go func(wg *sync.WaitGroup) { + for cell := range cellChan { + s.processCellCollisions(cell) + wg.Done() + } + }(wg) + } + + wg.Wait() + + s.processExitStates() +} +func (s *SpatialCollisionSystem) Destroy() {} + +func (s *SpatialCollisionSystem) processCellCollisions(cell *ecs.GridCell) { + // Get current cell's entities and nearby cells' entities + nearbyEntities := s.spatialGrid.GetNearbyEntities(cell.Key) + + // Split into batches within cell + const entityBatchSize = 50 + for start := 0; start < len(cell.Entities); start += entityBatchSize { + end := start + entityBatchSize + if end > len(cell.Entities) { + end = len(cell.Entities) + } + + // Process entity batch + for _, entity := range cell.Entities[start:end] { + s.checkEntityCollisions(entity, nearbyEntities) + } + } +} + +func (s *SpatialCollisionSystem) checkEntityCollisions(entity ecs.Entity, candidates []ecs.Entity) { + // Get components once + posA := s.Positions.Get(entity) + colliderA := s.GenericCollider.Get(entity) + + // Check against candidate entities + for _, other := range candidates { + //// Ensure each pair is checked only once + if entity >= other { + continue + } + + // Fast ID-based rejection + if entity == other { + continue + } + + //Get other components + posB := s.Positions.Get(other) + + //Broadphase check + if !s.spatialGrid.BoundingBoxesIntersect(float64(posA.X), float64(posA.Y), float64(posB.X), float64(posB.Y)) { + continue + } + + // Narrowphase check + switch colliderA.Shape { + case stdcomponents.BoxColliderShape: + s.boxToXCollision(entity, other) + case stdcomponents.CircleColliderShape: + s.circleToXCollision(entity, other) + default: + panic("Unknown collision shape") + } + } +} + +func (s *SpatialCollisionSystem) registerCollision(entityA, entityB ecs.Entity) { + pair := CollisionPair{entityA, entityB}.Normalize() + + s.currentCollisionsMx.Lock() + s.currentCollisions[pair] = struct{}{} + s.currentCollisionsMx.Unlock() + + s.activeCollisionsMx.Lock() + defer s.activeCollisionsMx.Unlock() + + // Create proxy entity for new collisions + if _, exists := s.activeCollisions[pair]; !exists { + proxy := s.EntityManager.Create() + s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) + s.activeCollisions[pair] = proxy + } else { + s.Collisions.Get(s.activeCollisions[pair]).State = stdcomponents.CollisionStateStay + } +} + +func (s *SpatialCollisionSystem) processExitStates() { + s.activeCollisionsMx.Lock() + defer s.activeCollisionsMx.Unlock() + s.currentCollisionsMx.RLock() + defer s.currentCollisionsMx.RUnlock() + + for pair, proxy := range s.activeCollisions { + if _, exists := s.currentCollisions[pair]; !exists { + collision := s.Collisions.Get(proxy) + if collision.State == stdcomponents.CollisionStateExit { + delete(s.activeCollisions, pair) + s.EntityManager.Delete(proxy) + } else { + collision.State = stdcomponents.CollisionStateExit + } + } + } +} + +func (s *SpatialCollisionSystem) boxToXCollision(entityA, entityB ecs.Entity) { + position1 := s.Positions.Get(entityA) + collider1 := s.BoxColliders.Get(entityA) + + position2 := s.Positions.Get(entityB) + genericCollider := s.GenericCollider.Get(entityB) + boxCollider := s.BoxColliders.Get(entityB) + + switch genericCollider.Shape { + case stdcomponents.BoxColliderShape: + // Check AABB collision + if !(position1.X+collider1.Width < position2.X || + position1.X > position2.X+boxCollider.Width || + position1.Y+collider1.Height < position2.Y || + position1.Y > position2.Y+boxCollider.Height) { + + s.registerCollision(entityA, entityB) + } + case stdcomponents.CircleColliderShape: + panic("Circle-Box collision not implemented") + default: + panic("Unknown collision shape") + } +} + +func (s *SpatialCollisionSystem) circleToXCollision(entityA, entityB ecs.Entity) { + panic("Circle-X collision not implemented") +} diff --git a/stdsystems/render.go b/stdsystems/render.go index f14ab5a1..4d4a8f9a 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -42,6 +42,7 @@ type RenderSystem struct { AnimationStates *stdcomponents.AnimationStateComponentManager SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager + ColliderBoxes *stdcomponents.ColliderBoxComponentManager renderList []RenderEntry instanceData []stdcomponents.RLTexturePro camera rl.Camera2D @@ -73,7 +74,22 @@ func (s *RenderSystem) Run(dt time.Duration) bool { rl.BeginDrawing() rl.ClearBackground(rl.Black) + // draw grid + const gridSize = 256 + for i := int32(1); i < 1024/gridSize; i++ { + rl.DrawLine(i*gridSize, 0, i*gridSize, 768, rl.Green) + } + for i := int32(1); i < 768/gridSize; i++ { + rl.DrawLine(0, i*gridSize, 1024, i*gridSize, rl.Green) + } s.render() + s.ColliderBoxes.EachEntity(func(e ecs.Entity) bool { + box := s.ColliderBoxes.Get(e) + pos := s.Positions.Get(e) + + rl.DrawRectangleLines(int32(pos.X), int32(pos.Y), int32(box.Width), int32(box.Height), rl.Red) + return true + }) rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) rl.DrawFPS(10, 10) rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) From 6015d46c729e33d01257f81f6dce94c04492d3f7 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 7 Mar 2025 22:03:00 +0300 Subject: [PATCH 035/196] feat collision-detection.go --- examples/new-api/config/config.go | 23 ++ examples/new-api/entities/player.go | 3 + examples/new-api/game.go | 2 +- examples/new-api/instances/component-list.go | 2 + examples/new-api/instances/system-list.go | 12 +- examples/new-api/scenes/main-scene.go | 6 +- examples/new-api/systems/player.go | 5 +- pkg/ecs/paged-array.go | 28 +-- pkg/ecs/paged-map.go | 89 ++++---- stdcomponents/colliders.go | 12 +- stdcomponents/ids.go | 1 + stdcomponents/spatial-index.go | 27 +++ stdsystems/collision-detection.go | 196 +++++++++++++++++ stdsystems/collision-spatial.go | 208 ------------------- stdsystems/render.go | 6 + 15 files changed, 347 insertions(+), 273 deletions(-) create mode 100644 examples/new-api/config/config.go create mode 100644 stdcomponents/spatial-index.go create mode 100644 stdsystems/collision-detection.go delete mode 100644 stdsystems/collision-spatial.go diff --git a/examples/new-api/config/config.go b/examples/new-api/config/config.go new file mode 100644 index 00000000..ef1b7375 --- /dev/null +++ b/examples/new-api/config/config.go @@ -0,0 +1,23 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package config + +import "gomp/stdcomponents" + +const ( + DefaultCollisionLayer stdcomponents.CollisionLayer = iota + PlayerCollisionLayer + EnemyCollisionLayer +) diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index d1fe7150..7360f870 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -7,6 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package entities import ( + "gomp/examples/new-api/config" "gomp/examples/new-api/sprites" "gomp/pkg/ecs" "gomp/stdcomponents" @@ -116,6 +117,8 @@ func CreatePlayer( // Adding GenericCollider component player.GenericCollider = genericColliders.Create(entity, stdcomponents.GenericCollider{ Shape: stdcomponents.BoxColliderShape, + Layer: config.EnemyCollisionLayer, + Mask: 1 << config.PlayerCollisionLayer, }) return player diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 5e496780..5e8efd2d 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -28,5 +28,5 @@ func main() { game.CurrentSceneId = scenes.MainSceneId engine := gomp.NewEngine(&game) - engine.Run(50, 0) + engine.Run(20, 0) } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index 7046646d..beec3649 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -38,6 +38,7 @@ type ComponentList struct { ColliderBox stdcomponents.ColliderBoxComponentManager ColliderCircle stdcomponents.ColliderCircleComponentManager Collision stdcomponents.CollisionComponentManager + SpatialIndex stdcomponents.SpatialIndexComponentManager Health components.HealthComponentManager Controller components.ControllerComponentManager @@ -64,6 +65,7 @@ func NewComponentList() ComponentList { ColliderBox: stdcomponents.NewColliderBoxComponentManager(), ColliderCircle: stdcomponents.NewColliderCircleComponentManager(), Collision: stdcomponents.NewCollisionComponentManager(), + SpatialIndex: stdcomponents.NewSpatialIndexComponentManager(), Health: components.NewHealthComponentManager(), Controller: components.NewControllerComponentManager(), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index 6d75f4a8..1b1a3cf6 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -36,10 +36,10 @@ func NewSystemList() SystemList { SpriteMatrix: stdsystems.NewSpriteMatrixSystem(), AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{assets.Textures}), YSort: stdsystems.NewYSortSystem(), - Collision: stdsystems.NewCollisionSystem(), - SpatialCollision: stdsystems.NewSpatialCollisionSystem(), - CollisionHandler: systems.NewCollisionHandlerSystem(), + CollisionDetection: stdsystems.NewCollisionDetectionSystem(), Render: stdsystems.NewRenderSystem(), + + CollisionHandler: systems.NewCollisionHandlerSystem(), } return newSystemList @@ -59,8 +59,8 @@ type SystemList struct { SpriteMatrix stdsystems.SpriteMatrixSystem AssetLib stdsystems.AssetLibSystem YSort stdsystems.YSortSystem - Collision stdsystems.CollisionSystem - CollisionHandler systems.CollisionHandlerSystem - SpatialCollision stdsystems.SpatialCollisionSystem + CollisionDetection stdsystems.CollisionDetectionSystem Render stdsystems.RenderSystem + + CollisionHandler systems.CollisionHandlerSystem } diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index 86878ee3..a5a0b515 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -47,7 +47,7 @@ func (s *MainScene) Init() { s.World.Systems.Player.Init() s.World.Systems.Velocity.Init() - s.World.Systems.Collision.Init() + s.World.Systems.CollisionDetection.Init() // Network patches s.World.Systems.NetworkSend.Init() @@ -78,7 +78,7 @@ func (s *MainScene) FixedUpdate(dt time.Duration) { s.World.Systems.Network.Run(dt) s.World.Systems.Velocity.Run(dt) - s.World.Systems.SpatialCollision.Run(dt) + s.World.Systems.CollisionDetection.Run(dt) s.World.Systems.CollisionHandler.Run(dt) s.World.Systems.NetworkSend.Run(dt) } @@ -108,7 +108,7 @@ func (s *MainScene) Destroy() { s.World.Systems.Player.Destroy() - s.World.Systems.Collision.Destroy() + s.World.Systems.CollisionDetection.Destroy() // Network patches s.World.Systems.NetworkSend.Destroy() diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index daa847dd..6092d500 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -9,6 +9,7 @@ package systems import ( rl "github.com/gen2brain/raylib-go/raylib" "gomp/examples/new-api/components" + "gomp/examples/new-api/config" "gomp/examples/new-api/entities" "gomp/examples/new-api/sprites" "gomp/pkg/ecs" @@ -44,7 +45,7 @@ type PlayerSystem struct { func (s *PlayerSystem) Init() { s.SpriteMatrixes.Create(sprites.PlayerSpriteSharedComponentId, sprites.PlayerSpriteMatrix) - for range 50 { + for range 5_000 { npc := entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, @@ -63,6 +64,8 @@ func (s *PlayerSystem) Init() { ) player.Position.X = 100 player.Position.Y = 100 + player.GenericCollider.Layer = config.PlayerCollisionLayer + player.GenericCollider.Mask = 1 << config.EnemyCollisionLayer s.Controllers.Create(player.Entity, components.Controller{}) s.Players.Create(player.Entity, components.PlayerTag{}) diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 654766ec..4545549a 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -21,7 +21,7 @@ type PagedArray[T any] struct { } func NewPagedArray[T any]() (a PagedArray[T]) { - a.book = make([]ArrayPage[T], 2, book_size) + a.book = make([]ArrayPage[T], 2, initialBookSize) a.parallelCount = uint8(runtime.NumCPU()) / 2 return a @@ -74,7 +74,7 @@ func (a *PagedArray[T]) Append(value T) *T { page.data[page.len] = value result := &page.data[page.len] page.len++ - if page.len == page_size { + if page.len == pageSize { a.currentPageIndex++ } a.len++ @@ -148,11 +148,11 @@ func (a *PagedArray[T]) Raw(result []T) []T { } func (a *PagedArray[T]) getPageIdAndIndex(index int) (int, int) { - pageId := index >> page_size_shift + pageId := index >> pageSizeShift assert.True(pageId < len(a.book), "index out of range") - index %= page_size - assert.True(index < page_size, "index out of range") + index %= pageSize + assert.True(index < pageSize, "index out of range") return pageId, index } @@ -169,7 +169,7 @@ func (a *PagedArray[T]) All(yield func(int, *T) bool) { for i := a.currentPageIndex; i >= 0; i-- { page = &book[i] - index_offset = i << page_size_shift + index_offset = i << pageSizeShift for j := page.len - 1; j >= 0; j-- { if !yield(index_offset+j, &page.data[j]) { @@ -181,14 +181,14 @@ func (a *PagedArray[T]) All(yield func(int, *T) bool) { func (a *PagedArray[T]) AllParallel(yield func(int, *T) bool) { var page *ArrayPage[T] - var data *[page_size]T + var data *[pageSize]T var index_offset int book := a.book wg := new(sync.WaitGroup) gorutineBudget := a.parallelCount - runner := func(data *[page_size]T, offset int, startIndex int, wg *sync.WaitGroup) { + runner := func(data *[pageSize]T, offset int, startIndex int, wg *sync.WaitGroup) { defer wg.Done() for j := startIndex; j >= 0; j-- { if !yield(offset+j, &(data[j])) { @@ -205,7 +205,7 @@ func (a *PagedArray[T]) AllParallel(yield func(int, *T) bool) { for i := a.currentPageIndex; i >= 0; i-- { page = &book[i] data = &page.data - index_offset = int(i) << page_size_shift + index_offset = int(i) << pageSizeShift if gorutineBudget > 0 { go runner(data, index_offset, page.len-1, wg) @@ -261,12 +261,12 @@ func (a *PagedArray[T]) AllDataValue(yield func(T) bool) { func (a *PagedArray[T]) AllDataValueParallel(yield func(T) bool) { var page *ArrayPage[T] - var data *[page_size]T + var data *[pageSize]T book := a.book wg := new(sync.WaitGroup) gorutineBudget := a.parallelCount - runner := func(data *[page_size]T, startIndex int, wg *sync.WaitGroup) { + runner := func(data *[pageSize]T, startIndex int, wg *sync.WaitGroup) { defer wg.Done() for j := startIndex; j >= 0; j-- { if !yield((data[j])) { @@ -297,12 +297,12 @@ func (a *PagedArray[T]) AllDataValueParallel(yield func(T) bool) { func (a *PagedArray[T]) AllDataParallel(yield func(*T) bool) { var page *ArrayPage[T] - var data *[page_size]T + var data *[pageSize]T book := a.book wg := new(sync.WaitGroup) gorutineBudget := a.parallelCount - runner := func(data *[page_size]T, startIndex int, wg *sync.WaitGroup) { + runner := func(data *[pageSize]T, startIndex int, wg *sync.WaitGroup) { defer wg.Done() for j := startIndex; j >= 0; j-- { if !yield(&(data[j])) { @@ -338,5 +338,5 @@ type SlicePage[T any] struct { type ArrayPage[T any] struct { len int - data [page_size]T + data [pageSize]T } diff --git a/pkg/ecs/paged-map.go b/pkg/ecs/paged-map.go index 24a781a3..10247a18 100644 --- a/pkg/ecs/paged-map.go +++ b/pkg/ecs/paged-map.go @@ -6,21 +6,19 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs -import ( - "github.com/negrel/assert" -) - const ( - page_size_shift int = 12 - page_size int = 1 << page_size_shift - book_size int = 1 << 2 + pageSizeShift = 12 + pageSize = 1 << pageSizeShift + initialBookSize = 4 // Starting with a small initial book size ) type MapPage[K Entity | SharedComponentInstanceId, V any] map[K]V + type PagedMap[K Entity | SharedComponentInstanceId, V any] struct { len int book []SlicePage[MapValue[V]] } + type MapValue[V any] struct { value V ok bool @@ -28,65 +26,78 @@ type MapValue[V any] struct { func NewPagedMap[K Entity | SharedComponentInstanceId, V any]() PagedMap[K, V] { return PagedMap[K, V]{ - book: make([]SlicePage[MapValue[V]], book_size), + book: make([]SlicePage[MapValue[V]], 0, initialBookSize), } } func (m *PagedMap[K, V]) Get(key K) (value V, ok bool) { - page_id, index := m.getPageIdAndIndex(key) - if page_id >= cap(m.book) { + pageID, index := m.getPageIDAndIndex(key) + if pageID >= len(m.book) { return value, false } - page := m.book[page_id] + page := m.book[pageID] if page.data == nil { return value, false } - if index >= cap(page.data) { - return value, false - } d := page.data[index] return d.value, d.ok } func (m *PagedMap[K, V]) Set(key K, value V) { - page_id, index := m.getPageIdAndIndex(key) - if page_id >= cap(m.book) { - // extend the pages slice - new_pages := make([]SlicePage[MapValue[V]], cap(m.book)*2) - m.book = append(m.book, new_pages...) - m.Set(key, value) - return + pageID, index := m.getPageIDAndIndex(key) + if pageID >= len(m.book) { + m.expandBook(pageID + 1) } - page := m.book[page_id] + page := &m.book[pageID] if page.data == nil { - page.data = make([]MapValue[V], page_size) - m.book[page_id] = page + page.data = make([]MapValue[V], pageSize) } - d := &page.data[index] - if !d.ok { + entry := &page.data[index] + if !entry.ok { m.len++ - d.ok = true + entry.ok = true } - d.value = value + entry.value = value } func (m *PagedMap[K, V]) Delete(key K) { - page_id, index := m.getPageIdAndIndex(key) - // Do not attempt to delete a value that does not exist - assert.True(page_id < cap(m.book)) - page := &m.book[page_id] - // Do not attempt to delete a value that does not exist - assert.True(page != nil) - page.data[index].ok = false - m.len-- + pageID, index := m.getPageIDAndIndex(key) + if pageID >= len(m.book) { + return + } + page := &m.book[pageID] + if page.data == nil { + return + } + entry := &page.data[index] + if entry.ok { + entry.ok = false + m.len-- + } } -func (m *PagedMap[K, V]) getPageIdAndIndex(key K) (page_id int, index int) { - page_id = int(key) >> page_size_shift - index = int(key) % page_size +func (m *PagedMap[K, V]) getPageIDAndIndex(key K) (pageID int, index int) { + // Convert key to uint64 to handle large values safely + u := uint64(key) + pageID = int(u >> pageSizeShift) + index = int(u % pageSize) return } +func (m *PagedMap[K, V]) expandBook(minLen int) { + if minLen <= cap(m.book) { + m.book = m.book[:minLen] + return + } + newCap := minLen + if newCap < 2*cap(m.book) { + newCap = 2 * cap(m.book) + } + newBook := make([]SlicePage[MapValue[V]], minLen, newCap) + copy(newBook, m.book) + m.book = newBook +} + func (m *PagedMap[K, V]) Len() int { return m.len } diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index c291ed38..5d305561 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -16,7 +16,7 @@ package stdcomponents import "gomp/pkg/ecs" -type ColliderShape = uint8 +type ColliderShape uint8 const ( InvalidColliderShape ColliderShape = iota @@ -24,6 +24,14 @@ const ( CircleColliderShape ) +type CollisionMask uint64 + +type CollisionLayer = CollisionMask + +const ( + ColliderLayerNone CollisionLayer = 0 +) + type ColliderBox struct { Width float32 Height float32 @@ -47,6 +55,8 @@ func NewColliderCircleComponentManager() ColliderCircleComponentManager { type GenericCollider struct { Shape ColliderShape + Layer CollisionLayer + Mask CollisionMask } type GenericColliderComponentManager = ecs.ComponentManager[GenericCollider] diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index e1197c1d..57d6eef2 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -32,5 +32,6 @@ const ( ColliderBoxComponentId ColliderCircleComponentId CollisionComponentId + SpatialIndexComponentId StdComponentIds ) diff --git a/stdcomponents/spatial-index.go b/stdcomponents/spatial-index.go new file mode 100644 index 00000000..745bb09e --- /dev/null +++ b/stdcomponents/spatial-index.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type SpatialIndex struct { + X, Y int +} + +type SpatialIndexComponentManager = ecs.ComponentManager[SpatialIndex] + +func NewSpatialIndexComponentManager() SpatialIndexComponentManager { + return ecs.NewComponentManager[SpatialIndex](SpatialIndexComponentId) +} diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go new file mode 100644 index 00000000..37bc20ba --- /dev/null +++ b/stdsystems/collision-detection.go @@ -0,0 +1,196 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewCollisionDetectionSystem() CollisionDetectionSystem { + return CollisionDetectionSystem{ + cellSizeX: 96, + cellSizeY: 128, + } +} + +type CollisionDetectionSystem struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.ColliderBoxComponentManager + Collisions *stdcomponents.CollisionComponentManager + SpatialIndex *stdcomponents.SpatialIndexComponentManager + + activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities + currentCollisions map[CollisionPair]struct{} + cellSizeX int + cellSizeY int + + spatialBuckets map[stdcomponents.SpatialIndex][]ecs.Entity + aabbs map[ecs.Entity]AABB + entityToCell map[ecs.Entity]stdcomponents.SpatialIndex +} + +type AABB struct { + Left, Right, Top, Bottom float32 +} + +func (s *CollisionDetectionSystem) Init() { + s.activeCollisions = make(map[CollisionPair]ecs.Entity) +} +func (s *CollisionDetectionSystem) Run(dt time.Duration) { + s.entityToCell = make(map[ecs.Entity]stdcomponents.SpatialIndex, s.GenericCollider.Len()) + s.spatialBuckets = make(map[stdcomponents.SpatialIndex][]ecs.Entity, 32) + + // Build spatial buckets and entity-to-cell map + s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + position := s.Positions.Get(entity) + cellX := int(position.X) / s.cellSizeX + cellY := int(position.Y) / s.cellSizeY + cell := stdcomponents.SpatialIndex{X: cellX, Y: cellY} + s.entityToCell[entity] = cell + s.spatialBuckets[cell] = append(s.spatialBuckets[cell], entity) + return true + }) + + // Precompute AABBs for box colliders + s.aabbs = make(map[ecs.Entity]AABB, s.BoxColliders.Len()) + s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { + position := s.Positions.Get(entity) + collider := s.BoxColliders.Get(entity) + s.aabbs[entity] = AABB{ + Left: position.X, + Right: position.X + collider.Width, + Top: position.Y, + Bottom: position.Y + collider.Height, + } + return true + }) + + s.currentCollisions = make(map[CollisionPair]struct{}) + + s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + collider := s.GenericCollider.Get(entity) + + switch collider.Shape { + case stdcomponents.BoxColliderShape: + s.boxToXCollision(entity) + case stdcomponents.CircleColliderShape: + s.circleToXCollision(entity) + default: + panic("Unknown collider shape") + } + return true + }) + + s.processExitStates() +} +func (s *CollisionDetectionSystem) Destroy() {} + +func (s *CollisionDetectionSystem) registerCollision(entityA, entityB ecs.Entity, posX, posY float32) { + pair := CollisionPair{entityA, entityB}.Normalize() + + s.currentCollisions[pair] = struct{}{} + + // Create proxy entity for new collisions + if _, exists := s.activeCollisions[pair]; !exists { + proxy := s.EntityManager.Create() + s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) + s.Positions.Create(proxy, stdcomponents.Position{X: posX, Y: posY}) + s.activeCollisions[pair] = proxy + } else { + s.Collisions.Get(s.activeCollisions[pair]).State = stdcomponents.CollisionStateStay + s.Positions.Get(s.activeCollisions[pair]).X = posX + s.Positions.Get(s.activeCollisions[pair]).Y = posY + } +} + +func (s *CollisionDetectionSystem) processExitStates() { + for pair, proxy := range s.activeCollisions { + if _, exists := s.currentCollisions[pair]; !exists { + collision := s.Collisions.Get(proxy) + if collision.State == stdcomponents.CollisionStateExit { + delete(s.activeCollisions, pair) + s.EntityManager.Delete(proxy) + } else { + collision.State = stdcomponents.CollisionStateExit + } + } + } +} + +func (s *CollisionDetectionSystem) boxToXCollision(entityA ecs.Entity) { + position1 := s.Positions.Get(entityA) + spatialIndex1 := s.entityToCell[entityA] + genericCollider1 := s.GenericCollider.Get(entityA) + + var nearByIndexes = [9]stdcomponents.SpatialIndex{ + {spatialIndex1.X, spatialIndex1.Y}, + {spatialIndex1.X - 1, spatialIndex1.Y}, + {spatialIndex1.X + 1, spatialIndex1.Y}, + {spatialIndex1.X, spatialIndex1.Y - 1}, + {spatialIndex1.X, spatialIndex1.Y + 1}, + {spatialIndex1.X - 1, spatialIndex1.Y - 1}, + {spatialIndex1.X + 1, spatialIndex1.Y + 1}, + {spatialIndex1.X - 1, spatialIndex1.Y + 1}, + {spatialIndex1.X + 1, spatialIndex1.Y - 1}, + } + + for _, spatialIndex := range nearByIndexes { + bucket := s.spatialBuckets[spatialIndex] + for _, entityB := range bucket { + if entityA >= entityB { + continue // Skip duplicate checks + } + + // Broad Phase + genericCollider2 := s.GenericCollider.Get(entityB) + if genericCollider1.Mask&(1< aabbB.Right { + continue + } + // Then Y-axis + if aabbA.Bottom < aabbB.Top || aabbA.Top > aabbB.Bottom { + continue + } + + s.registerCollision(entityA, entityB, (position1.X+position2.X)/2, (position1.Y+position2.Y)/2) + case stdcomponents.CircleColliderShape: + panic("Circle-Box collision not implemented") + default: + panic("Unknown collision shape") + } + } + } +} + +func (s *CollisionDetectionSystem) circleToXCollision(entityA ecs.Entity) { + panic("Circle-X collision not implemented") +} diff --git a/stdsystems/collision-spatial.go b/stdsystems/collision-spatial.go deleted file mode 100644 index 727898de..00000000 --- a/stdsystems/collision-spatial.go +++ /dev/null @@ -1,208 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" - "runtime" - "sync" - "time" -) - -func NewSpatialCollisionSystem() SpatialCollisionSystem { - return SpatialCollisionSystem{ - activeCollisions: make(map[CollisionPair]ecs.Entity), - } -} - -type SpatialCollisionSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - GenericCollider *stdcomponents.GenericColliderComponentManager - BoxColliders *stdcomponents.ColliderBoxComponentManager - Collisions *stdcomponents.CollisionComponentManager - - activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities - currentCollisions map[CollisionPair]struct{} - currentCollisionsMx sync.RWMutex - activeCollisionsMx sync.Mutex - - spatialGrid ecs.SpatialGrid -} - -func (s *SpatialCollisionSystem) Init() {} -func (s *SpatialCollisionSystem) Run(dt time.Duration) { - s.currentCollisions = make(map[CollisionPair]struct{}) - s.spatialGrid = ecs.NewSpatialGrid(256) - - s.Positions.EachEntity(func(entity ecs.Entity) bool { - position := s.Positions.Get(entity) - s.spatialGrid.AddEntity(entity, float64(position.X), float64(position.Y)) - return true - }) - - // 1. Get all non-empty cells from spatial grid - cells := s.spatialGrid.GetActiveCells() - - // 2. Process cells in parallel batches - wg := new(sync.WaitGroup) - wg.Add(len(cells)) - - cellChan := make(chan *ecs.GridCell, len(cells)) - for i := range cells { - cellChan <- &cells[i] - } - defer close(cellChan) - - // Worker pool pattern - for i := 0; i < runtime.NumCPU(); i++ { - go func(wg *sync.WaitGroup) { - for cell := range cellChan { - s.processCellCollisions(cell) - wg.Done() - } - }(wg) - } - - wg.Wait() - - s.processExitStates() -} -func (s *SpatialCollisionSystem) Destroy() {} - -func (s *SpatialCollisionSystem) processCellCollisions(cell *ecs.GridCell) { - // Get current cell's entities and nearby cells' entities - nearbyEntities := s.spatialGrid.GetNearbyEntities(cell.Key) - - // Split into batches within cell - const entityBatchSize = 50 - for start := 0; start < len(cell.Entities); start += entityBatchSize { - end := start + entityBatchSize - if end > len(cell.Entities) { - end = len(cell.Entities) - } - - // Process entity batch - for _, entity := range cell.Entities[start:end] { - s.checkEntityCollisions(entity, nearbyEntities) - } - } -} - -func (s *SpatialCollisionSystem) checkEntityCollisions(entity ecs.Entity, candidates []ecs.Entity) { - // Get components once - posA := s.Positions.Get(entity) - colliderA := s.GenericCollider.Get(entity) - - // Check against candidate entities - for _, other := range candidates { - //// Ensure each pair is checked only once - if entity >= other { - continue - } - - // Fast ID-based rejection - if entity == other { - continue - } - - //Get other components - posB := s.Positions.Get(other) - - //Broadphase check - if !s.spatialGrid.BoundingBoxesIntersect(float64(posA.X), float64(posA.Y), float64(posB.X), float64(posB.Y)) { - continue - } - - // Narrowphase check - switch colliderA.Shape { - case stdcomponents.BoxColliderShape: - s.boxToXCollision(entity, other) - case stdcomponents.CircleColliderShape: - s.circleToXCollision(entity, other) - default: - panic("Unknown collision shape") - } - } -} - -func (s *SpatialCollisionSystem) registerCollision(entityA, entityB ecs.Entity) { - pair := CollisionPair{entityA, entityB}.Normalize() - - s.currentCollisionsMx.Lock() - s.currentCollisions[pair] = struct{}{} - s.currentCollisionsMx.Unlock() - - s.activeCollisionsMx.Lock() - defer s.activeCollisionsMx.Unlock() - - // Create proxy entity for new collisions - if _, exists := s.activeCollisions[pair]; !exists { - proxy := s.EntityManager.Create() - s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) - s.activeCollisions[pair] = proxy - } else { - s.Collisions.Get(s.activeCollisions[pair]).State = stdcomponents.CollisionStateStay - } -} - -func (s *SpatialCollisionSystem) processExitStates() { - s.activeCollisionsMx.Lock() - defer s.activeCollisionsMx.Unlock() - s.currentCollisionsMx.RLock() - defer s.currentCollisionsMx.RUnlock() - - for pair, proxy := range s.activeCollisions { - if _, exists := s.currentCollisions[pair]; !exists { - collision := s.Collisions.Get(proxy) - if collision.State == stdcomponents.CollisionStateExit { - delete(s.activeCollisions, pair) - s.EntityManager.Delete(proxy) - } else { - collision.State = stdcomponents.CollisionStateExit - } - } - } -} - -func (s *SpatialCollisionSystem) boxToXCollision(entityA, entityB ecs.Entity) { - position1 := s.Positions.Get(entityA) - collider1 := s.BoxColliders.Get(entityA) - - position2 := s.Positions.Get(entityB) - genericCollider := s.GenericCollider.Get(entityB) - boxCollider := s.BoxColliders.Get(entityB) - - switch genericCollider.Shape { - case stdcomponents.BoxColliderShape: - // Check AABB collision - if !(position1.X+collider1.Width < position2.X || - position1.X > position2.X+boxCollider.Width || - position1.Y+collider1.Height < position2.Y || - position1.Y > position2.Y+boxCollider.Height) { - - s.registerCollision(entityA, entityB) - } - case stdcomponents.CircleColliderShape: - panic("Circle-Box collision not implemented") - default: - panic("Unknown collision shape") - } -} - -func (s *SpatialCollisionSystem) circleToXCollision(entityA, entityB ecs.Entity) { - panic("Circle-X collision not implemented") -} diff --git a/stdsystems/render.go b/stdsystems/render.go index 4d4a8f9a..284314e6 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -43,6 +43,7 @@ type RenderSystem struct { SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager ColliderBoxes *stdcomponents.ColliderBoxComponentManager + Collisions *stdcomponents.CollisionComponentManager renderList []RenderEntry instanceData []stdcomponents.RLTexturePro camera rl.Camera2D @@ -90,6 +91,11 @@ func (s *RenderSystem) Run(dt time.Duration) bool { rl.DrawRectangleLines(int32(pos.X), int32(pos.Y), int32(box.Width), int32(box.Height), rl.Red) return true }) + s.Collisions.EachEntity(func(entity ecs.Entity) bool { + pos := s.Positions.Get(entity) + rl.DrawRectangle(int32(pos.X), int32(pos.Y), 16, 16, rl.Red) + return true + }) rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) rl.DrawFPS(10, 10) rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) From 08dfdf027d2b03d361f688b072d2d108add3ce21 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 8 Mar 2025 14:30:07 +0300 Subject: [PATCH 036/196] implement multithreading in collision-detection.go --- examples/new-api/systems/player.go | 2 +- stdsystems/collision-detection.go | 118 ++++++++++++++++++++++++----- 2 files changed, 99 insertions(+), 21 deletions(-) diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 6092d500..9a472a2a 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -45,7 +45,7 @@ type PlayerSystem struct { func (s *PlayerSystem) Init() { s.SpriteMatrixes.Create(sprites.PlayerSpriteSharedComponentId, sprites.PlayerSpriteMatrix) - for range 5_000 { + for range 16_000 { npc := entities.CreatePlayer( s.EntityManager, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 37bc20ba..dea69f16 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -17,13 +17,16 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" + "runtime" + "sync" "time" ) func NewCollisionDetectionSystem() CollisionDetectionSystem { return CollisionDetectionSystem{ - cellSizeX: 96, - cellSizeY: 128, + cellSizeX: 96, + cellSizeY: 128, + spatialBuckets: make(map[stdcomponents.SpatialIndex][]ecs.Entity, 32), } } @@ -52,9 +55,21 @@ type AABB struct { func (s *CollisionDetectionSystem) Init() { s.activeCollisions = make(map[CollisionPair]ecs.Entity) } + +type CollisionEvent struct { + entityA, entityB ecs.Entity + posX, posY float32 +} + func (s *CollisionDetectionSystem) Run(dt time.Duration) { - s.entityToCell = make(map[ecs.Entity]stdcomponents.SpatialIndex, s.GenericCollider.Len()) - s.spatialBuckets = make(map[stdcomponents.SpatialIndex][]ecs.Entity, 32) + if len(s.entityToCell) < s.GenericCollider.Len() { + s.entityToCell = make(map[ecs.Entity]stdcomponents.SpatialIndex, s.GenericCollider.Len()) + } + // Reuse spatialBuckets to reduce allocations + for k := range s.spatialBuckets { + delete(s.spatialBuckets, k) + } + s.currentCollisions = make(map[CollisionPair]struct{}) // Build spatial buckets and entity-to-cell map s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { @@ -81,21 +96,81 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { return true }) - s.currentCollisions = make(map[CollisionPair]struct{}) + // Create collision channel + collisionChan := make(chan CollisionEvent, 4096) + doneChan := make(chan struct{}) - s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { - collider := s.GenericCollider.Get(entity) - - switch collider.Shape { - case stdcomponents.BoxColliderShape: - s.boxToXCollision(entity) - case stdcomponents.CircleColliderShape: - s.circleToXCollision(entity) - default: - panic("Unknown collider shape") + // Start result collector + go func() { + for event := range collisionChan { + pair := CollisionPair{event.entityA, event.entityB}.Normalize() + s.currentCollisions[pair] = struct{}{} + + if _, exists := s.activeCollisions[pair]; !exists { + proxy := s.EntityManager.Create() + s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) + s.Positions.Create(proxy, stdcomponents.Position{X: event.posX, Y: event.posY}) + s.activeCollisions[pair] = proxy + } else { + proxy := s.activeCollisions[pair] + s.Collisions.Get(proxy).State = stdcomponents.CollisionStateStay + s.Positions.Get(proxy).X = event.posX + s.Positions.Get(proxy).Y = event.posY + } } - return true - }) + close(doneChan) + }() + + entities := s.GenericCollider.RawEntities(make([]ecs.Entity, 0, s.GenericCollider.Len())) + + // Worker pool setup + numWorkers := runtime.NumCPU() * 4 + var wg sync.WaitGroup + wg.Add(numWorkers) + + chunkSize := (len(entities) + numWorkers - 1) / numWorkers + for i := 0; i < numWorkers; i++ { + go func(workerID int) { + defer wg.Done() + start := workerID * chunkSize + end := start + chunkSize + if end > len(entities) { + end = len(entities) + } + + for _, entityA := range entities[start:end] { + collider := s.GenericCollider.Get(entityA) + + switch collider.Shape { + case stdcomponents.BoxColliderShape: + s.boxToXCollision(entityA, collisionChan) + case stdcomponents.CircleColliderShape: + s.circleToXCollision(entityA) + default: + panic("Unknown collider shape") + } + } + }(i) + } + + // Wait for workers and close collision channel + wg.Wait() + close(collisionChan) + <-doneChan // Wait for result collector + + //s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { + // collider := s.GenericCollider.Get(entity) + // + // switch collider.Shape { + // case stdcomponents.BoxColliderShape: + // s.boxToXCollision(entity) + // case stdcomponents.CircleColliderShape: + // s.circleToXCollision(entity) + // default: + // panic("Unknown collider shape") + // } + // return true + //}) s.processExitStates() } @@ -133,7 +208,7 @@ func (s *CollisionDetectionSystem) processExitStates() { } } -func (s *CollisionDetectionSystem) boxToXCollision(entityA ecs.Entity) { +func (s *CollisionDetectionSystem) boxToXCollision(entityA ecs.Entity, collisionChan chan<- CollisionEvent) { position1 := s.Positions.Get(entityA) spatialIndex1 := s.entityToCell[entityA] genericCollider1 := s.GenericCollider.Get(entityA) @@ -159,7 +234,8 @@ func (s *CollisionDetectionSystem) boxToXCollision(entityA ecs.Entity) { // Broad Phase genericCollider2 := s.GenericCollider.Get(entityB) - if genericCollider1.Mask&(1< Date: Sun, 9 Mar 2025 11:48:17 +0300 Subject: [PATCH 037/196] hot fix entity array out of bounds collision-detection.go --- stdsystems/collision-detection.go | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index dea69f16..353a6777 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -7,7 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +none :)dw Thank you for your support! */ @@ -38,14 +38,15 @@ type CollisionDetectionSystem struct { Collisions *stdcomponents.CollisionComponentManager SpatialIndex *stdcomponents.SpatialIndexComponentManager + cellSizeX int + cellSizeY int + + // Cache activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities currentCollisions map[CollisionPair]struct{} - cellSizeX int - cellSizeY int - - spatialBuckets map[stdcomponents.SpatialIndex][]ecs.Entity - aabbs map[ecs.Entity]AABB - entityToCell map[ecs.Entity]stdcomponents.SpatialIndex + spatialBuckets map[stdcomponents.SpatialIndex][]ecs.Entity + entityToCell map[ecs.Entity]stdcomponents.SpatialIndex + aabbs map[ecs.Entity]AABB } type AABB struct { @@ -129,14 +130,19 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { wg.Add(numWorkers) chunkSize := (len(entities) + numWorkers - 1) / numWorkers - for i := 0; i < numWorkers; i++ { - go func(workerID int) { + for workerId := 0; workerId < numWorkers; workerId++ { + startIndex := workerId * chunkSize + if startIndex >= len(entities) { // FIXME, probably chunkSize is invalid + wg.Done() + continue + } + endIndex := startIndex + chunkSize + if endIndex > len(entities) { + endIndex = len(entities) + } + + go func(start, end int) { defer wg.Done() - start := workerID * chunkSize - end := start + chunkSize - if end > len(entities) { - end = len(entities) - } for _, entityA := range entities[start:end] { collider := s.GenericCollider.Get(entityA) @@ -150,7 +156,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { panic("Unknown collider shape") } } - }(i) + }(startIndex, endIndex) } // Wait for workers and close collision channel From 92850e3d2be1a065db82c8b42e98d8b7417cebbb Mon Sep 17 00:00:00 2001 From: Kuzovlev Roman Date: Sun, 9 Mar 2025 22:35:36 +0700 Subject: [PATCH 038/196] fix collision-detection, set proper amount of workers, and check entites slice range --- stdsystems/collision-detection.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 353a6777..112b94f8 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -125,23 +125,23 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { entities := s.GenericCollider.RawEntities(make([]ecs.Entity, 0, s.GenericCollider.Len())) // Worker pool setup - numWorkers := runtime.NumCPU() * 4 var wg sync.WaitGroup - wg.Add(numWorkers) - - chunkSize := (len(entities) + numWorkers - 1) / numWorkers - for workerId := 0; workerId < numWorkers; workerId++ { - startIndex := workerId * chunkSize - if startIndex >= len(entities) { // FIXME, probably chunkSize is invalid - wg.Done() - continue - } + maxNumWorkers := runtime.NumCPU() * 4 + entitiesLength := len(entities) + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot entities + numWorkers := max(min(entitiesLength/10, maxNumWorkers), 1) + chunkSize := entitiesLength / numWorkers + + for i := 0; i < numWorkers; i++ { + wg.Add(1) + + startIndex := i * chunkSize endIndex := startIndex + chunkSize - if endIndex > len(entities) { - endIndex = len(entities) + if i == numWorkers-1 { // have to set endIndex to entites lenght, if last worker + endIndex = entitiesLength } - go func(start, end int) { + go func(start int, end int) { defer wg.Done() for _, entityA := range entities[start:end] { From ae4a1dc7d47112948cd6e78cfb1c78bc87b14023 Mon Sep 17 00:00:00 2001 From: Kuzovlev Roman Date: Sun, 9 Mar 2025 23:13:02 +0700 Subject: [PATCH 039/196] collision-detection do not process same entity in different workers --- stdsystems/collision-detection.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 112b94f8..75f8b1f3 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -136,7 +136,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { wg.Add(1) startIndex := i * chunkSize - endIndex := startIndex + chunkSize + endIndex := startIndex + chunkSize - 1 if i == numWorkers-1 { // have to set endIndex to entites lenght, if last worker endIndex = entitiesLength } From bd93006c95b458eef91f609c2ef2bac06b1e6e7a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 9 Mar 2025 22:14:39 +0300 Subject: [PATCH 040/196] feat assterodd game --- engine.go | 2 +- examples/new-api/assets/bullet.png | Bin 0 -> 208 bytes examples/new-api/assets/meteor_large.png | Bin 0 -> 629 bytes examples/new-api/assets/ship_E.png | Bin 0 -> 948 bytes examples/new-api/assets/wall.png | Bin 0 -> 1010 bytes .../components/asteroid-scene-manager.go | 28 ++ examples/new-api/components/asteroid-tag.go | 26 ++ examples/new-api/components/bullet-tag.go | 26 ++ examples/new-api/components/hp.go | 8 +- examples/new-api/components/ids.go | 7 + examples/new-api/components/space-spawner.go | 31 ++ .../new-api/components/spaceship-intent.go | 31 ++ examples/new-api/components/wall-tag.go | 26 ++ examples/new-api/components/weapon.go | 32 ++ examples/new-api/config/config.go | 1 + examples/new-api/entities/asteroid.go | 98 ++++++ examples/new-api/entities/bullet.go | 95 ++++++ examples/new-api/entities/player.go | 8 +- examples/new-api/entities/space-spawner.go | 52 +++ examples/new-api/entities/spaceship.go | 100 ++++++ examples/new-api/entities/wall.go | 85 +++++ examples/new-api/game.go | 3 +- examples/new-api/instances/component-list.go | 34 +- examples/new-api/instances/system-list.go | 20 +- examples/new-api/scenes/assterodd-scene.go | 164 +++++++++ examples/new-api/scenes/ids.go | 1 + examples/new-api/scenes/main-scene.go | 10 +- examples/new-api/scenes/scene-list.go | 6 +- examples/new-api/systems/asterodd.go | 157 +++++++++ examples/new-api/systems/collision-handler.go | 142 +++++++- examples/new-api/systems/hp.go | 60 ++++ examples/new-api/systems/player.go | 4 +- examples/new-api/systems/render-assterodd.go | 313 ++++++++++++++++++ .../new-api/systems/render-bogdan.go | 51 ++- examples/new-api/systems/space-spawner.go | 75 +++++ examples/new-api/systems/spaceship-intents.go | 129 ++++++++ game.go | 6 +- internal/sdl2/sdl-game.go | 2 +- internal/tomb-mates-demo/main.go | 2 +- render.go | 40 +++ stdcomponents/colliders.go | 30 +- stdcomponents/renderable.go | 1 + stdsystems/collider.go | 68 ++++ stdsystems/collision-detection.go | 26 +- stdsystems/collision.go | 2 +- stdsystems/rl-render.go | 214 ------------ stdsystems/sprite.go | 114 +++---- 47 files changed, 1952 insertions(+), 378 deletions(-) create mode 100644 examples/new-api/assets/bullet.png create mode 100644 examples/new-api/assets/meteor_large.png create mode 100644 examples/new-api/assets/ship_E.png create mode 100644 examples/new-api/assets/wall.png create mode 100644 examples/new-api/components/asteroid-scene-manager.go create mode 100644 examples/new-api/components/asteroid-tag.go create mode 100644 examples/new-api/components/bullet-tag.go create mode 100644 examples/new-api/components/space-spawner.go create mode 100644 examples/new-api/components/spaceship-intent.go create mode 100644 examples/new-api/components/wall-tag.go create mode 100644 examples/new-api/components/weapon.go create mode 100644 examples/new-api/entities/asteroid.go create mode 100644 examples/new-api/entities/bullet.go create mode 100644 examples/new-api/entities/space-spawner.go create mode 100644 examples/new-api/entities/spaceship.go create mode 100644 examples/new-api/entities/wall.go create mode 100644 examples/new-api/scenes/assterodd-scene.go create mode 100644 examples/new-api/systems/asterodd.go create mode 100644 examples/new-api/systems/hp.go create mode 100644 examples/new-api/systems/render-assterodd.go rename stdsystems/render.go => examples/new-api/systems/render-bogdan.go (82%) create mode 100644 examples/new-api/systems/space-spawner.go create mode 100644 examples/new-api/systems/spaceship-intents.go create mode 100644 render.go create mode 100644 stdsystems/collider.go delete mode 100644 stdsystems/rl-render.go diff --git a/engine.go b/engine.go index 1466f6b0..476e5751 100644 --- a/engine.go +++ b/engine.go @@ -75,7 +75,7 @@ func (e *Engine) Run(tickrate uint, framerate uint) { log.Println("Too many updates detected") } - // Render + // RenderAssterodd e.Game.Render(dt) } } diff --git a/examples/new-api/assets/bullet.png b/examples/new-api/assets/bullet.png new file mode 100644 index 0000000000000000000000000000000000000000..c0647535bb8064539ad33809f36ada693209b6f8 GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0L3?#3!&-4XSEa{HEjtmSN`?>!lvI6-$0X`wF z|G@yr1JbMvDTjb8=8_=4;Q8?khEi1>KyHGki(`nz>9>~@B@ZZwxF&vD!%`d8#Npzg zz}D@=lB5vu#`^MiRfmG7>-%=RGc9H>YpRm2*wtvl+v30QknRTuaSsDlrWz)>X$~!l u6Bcrc>@Yc_A1u}0c!y`o>pO|rKNx@QS(|V5m+L6dE(T9mKbLh*2~7ZYS4RW@ literal 0 HcmV?d00001 diff --git a/examples/new-api/assets/meteor_large.png b/examples/new-api/assets/meteor_large.png new file mode 100644 index 0000000000000000000000000000000000000000..2b996c057eab28bfef54470648f4ef7a2494b8dd GIT binary patch literal 629 zcmV-*0*d{KP)&)Uz=+RxA0&d%P?&)d$<-p|k60002+ z@bl;A?fLoo*x2Fr_W07$-TeIg;o<1*?eWjg+yDRn_SqVe0000DbW%=J&!4Z~uiu}~ zA5Sk|05{+jp#T5@zez+vR9M69m)VlSAP_}S(GFzX5|;n}(@6qRXu3P8@o~(XR-IE< zE`e74eE6uZ;2#LO!=d{?&|O@N?$Z5H&|SjBPlg~Vlz=CGHV6vE<14=yScPKoH=hiQ zLUDM>X9KBF3|{lAfly$94VWLVr6Jf2fo}Lm@TEb^0!#3rHds-h@$bLQpNFw9Xh~qn z^&g(qN?Jjnsr7&H*`RrWCHNTcxJ}CnG{x6-Ivhcv06d@TbSQ$V0}%Wj)#>U8stQ2x z&%vQOT@^u<0Z2Zq$I7ctS4L1p0ES-;?>b!(;VsYvKMra8-cX%Rw*pJ9zhC&rh`)EX z5gh6EJjus+Yy>KD28xfW5g5-Ht`zT0yNJLUC?1FwOxSJk!B86s_8NQuawnRw&oC8Z z=$8q0873Z>x6y!Im zBp$?@DMg?SiAMpN%N1Bd=3(P@&JkKe=5c^_!06AUA@k>c$B2^^zU5n-8M2bXBc8(9 zF>g`$lBaQD(3}P`xucpl#vw)^vB@Djc`?)Rrt|NYPO6>LAhaxD3e>O>3> P00000NkvXXu0mjfR^c~t literal 0 HcmV?d00001 diff --git a/examples/new-api/assets/ship_E.png b/examples/new-api/assets/ship_E.png new file mode 100644 index 0000000000000000000000000000000000000000..fb8910a64ad3a196534aa7ca674de929c50d1304 GIT binary patch literal 948 zcmYk4drVtZ9LG=X))83g22PYQ>_XN|hRpcL$Z%_m-3m@S!pNSDuozn%Xf@2oN^}LI z=+y8STc$`Zb1<_PgE-bi*9KE&Hn(|8X-nztrM-RA$Gv^f2YrBN@gIN5IluG${gU(h zd~?pX^&%lNw>TF7z^tnkG{DSzymIJpA5?Y(U|J>RzsUz+ygM&*d^G^H>iRbt_hhr# zR4TQ+yqr#F(&=dngpOT0I2eHJcqcPR`BQZ8k5CyI>K= zalPKE)mn^3!f3Q%m;*|Rig>+2zdz)3`kc-HK`aD=p zesM7#jmF`%kw`2QTAZ1gHy9vN6oQ#d_SxCToFo~6{Bv^-2pI^3=jYuR=7jE`BWMk> zL9+`Bq}{#%nH>(F%N1}s{T@#cx`22niuIPx4nw#dCmTcuWC%)eQ5nKTDH%m^Wil2* zp$LmYWGEMbJ*ZGvOvyu~`z5fp`JIt1yLe!zv3z2LQIX&Oz%_bH@hB1cA+dGS;oHP=CkzMCe3;8@dlo_NBO+GZ-34UQdYZ2)<>Jd zHN9AuK(VFcD)w2yIQm{Wr}@fc(}*$* zj^P@WSx~}Lv8jXaw{jSh#c{((^~ChMikHdts7!ykjP^h&ypz1NSeZLUZ?DW8luH=J zmEOSm{iAnMlJ7wtR#w?0XI=mWYN7bmsQd(|cvKp;9X8%v(;6%dhs4H9^!_|X{F`e< zA9N!vKw@qqrf%#gVY_$Pms!d~7Zn@Eh2p9Y<cF*CuVjbngW>{xW}r6K#@D(}U*@dSaTz<;T`0_d`X~5bK;0gZ JV6>*W^ItW6JC*1eMY(*$nL zY5!FK;eajV%-F?=kk}OMbmYW=JZc_>Hv&hFEyeQWPJ!c+nj|e(@h(uo7M>W%m3fO; zDqBJ#7Az5{pz}G39L1XPS~NN!B$&{wn3o zj$2XfU9C&+7M`rBKI6D*3p=;_UPa0I<_AaicVB2+*fTirw66Ec;}g}vl3n5B?-`V( zIGma7WiythYYhifD>n*TVpb><6XMtI3rmhnSsbyfq%vpWv3bS83w*3WyYdRR1{V1F zr>PH?ZTDbh&sqlY8|XCZ({l4)oO!b~FMzSrAJrvFgS$qbk9dzk_Br2OkC}1xyA@Mo3qq%i3B9je$G>_^i$7A2xTJNV^=rq&0|$pyM=yEZ zb$d+xEGqo)JUM)zZZClB&XBQBLv7a;)1oHs_E2)Tvtc#h5sS~%&+%)ykpGT}hx~D8 z5O@)NvjL|jfj4t`J$T#QegOK%7&fzR13XYa0(RSo0z;RZVc7DXXK4Hd_~qy2h?ZCG z1FMc5*wWlk4PN_165#A^FR)!bU^mx?0De@i08i@jM6?xNknhY$1g{`502MY|tO2jd z0$%BaH{ekV&tS?xvp1PM>*a=1(z^VHVhATc&jLe{h)zX@Dx^F#90TDu3x63|Z%?A+ zvM&(MG(`hGF%b=RSYIH9Ic*q*Hl@Sl>>dW!V!Jm|YR3PjoKAQ>CpA3>e_qCn29y5& HhN7B(c5U35 literal 0 HcmV?d00001 diff --git a/examples/new-api/components/asteroid-scene-manager.go b/examples/new-api/components/asteroid-scene-manager.go new file mode 100644 index 00000000..7e15b789 --- /dev/null +++ b/examples/new-api/components/asteroid-scene-manager.go @@ -0,0 +1,28 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +type AsteroidSceneManager struct { + PlayerScore int32 + PlayerHp int32 +} + +type AsteroidSceneManagerComponentManager = ecs.ComponentManager[AsteroidSceneManager] + +func NewAsteroidSceneManagerComponentManager() AsteroidSceneManagerComponentManager { + return ecs.NewComponentManager[AsteroidSceneManager](AsteroidSceneManagerComponentId) +} diff --git a/examples/new-api/components/asteroid-tag.go b/examples/new-api/components/asteroid-tag.go new file mode 100644 index 00000000..aefa29e5 --- /dev/null +++ b/examples/new-api/components/asteroid-tag.go @@ -0,0 +1,26 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +type AsteroidTag struct { +} + +type AsteroidComponentManager = ecs.ComponentManager[AsteroidTag] + +func NewAsteroidTagComponentManager() AsteroidComponentManager { + return ecs.NewComponentManager[AsteroidTag](AsteroidTagComponentId) +} diff --git a/examples/new-api/components/bullet-tag.go b/examples/new-api/components/bullet-tag.go new file mode 100644 index 00000000..b29b3f0c --- /dev/null +++ b/examples/new-api/components/bullet-tag.go @@ -0,0 +1,26 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +type BulletTag struct { +} + +type BulletTagComponentManager = ecs.ComponentManager[BulletTag] + +func NewBulletTagComponentManager() BulletTagComponentManager { + return ecs.NewComponentManager[BulletTag](BulletTagComponentId) +} diff --git a/examples/new-api/components/hp.go b/examples/new-api/components/hp.go index 49e63387..d5ed1e39 100644 --- a/examples/new-api/components/hp.go +++ b/examples/new-api/components/hp.go @@ -16,12 +16,12 @@ package components import "gomp/pkg/ecs" -type Health struct { +type Hp struct { Hp, MaxHp int32 } -type HealthComponentManager = ecs.ComponentManager[Health] +type HpComponentManager = ecs.ComponentManager[Hp] -func NewHealthComponentManager() HealthComponentManager { - return ecs.NewComponentManager[Health](HealthComponentId) +func NewHealthComponentManager() HpComponentManager { + return ecs.NewComponentManager[Hp](HealthComponentId) } diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go index 00fd9c3c..443d0952 100644 --- a/examples/new-api/components/ids.go +++ b/examples/new-api/components/ids.go @@ -22,4 +22,11 @@ const ( HealthComponentId = iota + stdcomponents.StdComponentIds ControllerComponentId PlayerTagComponentId + BulletTagComponentId + WallComponentId + SpaceSpawnerTagComponentId + AsteroidTagComponentId + WeaponComponentId + SpaceshipIntentComponentId + AsteroidSceneManagerComponentId ) diff --git a/examples/new-api/components/space-spawner.go b/examples/new-api/components/space-spawner.go new file mode 100644 index 00000000..30b0fd05 --- /dev/null +++ b/examples/new-api/components/space-spawner.go @@ -0,0 +1,31 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import ( + "gomp/pkg/ecs" + "time" +) + +type SpaceSpawnerTag struct { + Cooldown time.Duration + CooldownLeft time.Duration +} + +type SpaceSpawnerComponentManager = ecs.ComponentManager[SpaceSpawnerTag] + +func NewSpaceSpawnerTagComponentManager() SpaceSpawnerComponentManager { + return ecs.NewComponentManager[SpaceSpawnerTag](SpaceSpawnerTagComponentId) +} diff --git a/examples/new-api/components/spaceship-intent.go b/examples/new-api/components/spaceship-intent.go new file mode 100644 index 00000000..40a7a1ee --- /dev/null +++ b/examples/new-api/components/spaceship-intent.go @@ -0,0 +1,31 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +type SpaceshipIntent struct { + MoveUp bool + MoveDown bool + RotateLeft bool + RotateRight bool + Fire bool +} + +type SpaceshipIntentComponentManager = ecs.ComponentManager[SpaceshipIntent] + +func NewSpaceshipIntentComponentManager() SpaceshipIntentComponentManager { + return ecs.NewComponentManager[SpaceshipIntent](SpaceshipIntentComponentId) +} diff --git a/examples/new-api/components/wall-tag.go b/examples/new-api/components/wall-tag.go new file mode 100644 index 00000000..f06fd71a --- /dev/null +++ b/examples/new-api/components/wall-tag.go @@ -0,0 +1,26 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +type Wall struct { +} + +type WallTagComponentManager = ecs.ComponentManager[Wall] + +func NewWallComponentManager() WallTagComponentManager { + return ecs.NewComponentManager[Wall](WallComponentId) +} diff --git a/examples/new-api/components/weapon.go b/examples/new-api/components/weapon.go new file mode 100644 index 00000000..19c0e947 --- /dev/null +++ b/examples/new-api/components/weapon.go @@ -0,0 +1,32 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import ( + "gomp/pkg/ecs" + "time" +) + +type Weapon struct { + Damage int + Cooldown time.Duration + CooldownLeft time.Duration +} + +type WeaponComponentManager = ecs.ComponentManager[Weapon] + +func NewWeaponComponentManager() WeaponComponentManager { + return ecs.NewComponentManager[Weapon](WeaponComponentId) +} diff --git a/examples/new-api/config/config.go b/examples/new-api/config/config.go index ef1b7375..6a904ab6 100644 --- a/examples/new-api/config/config.go +++ b/examples/new-api/config/config.go @@ -20,4 +20,5 @@ const ( DefaultCollisionLayer stdcomponents.CollisionLayer = iota PlayerCollisionLayer EnemyCollisionLayer + WallCollisionLayer ) diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go new file mode 100644 index 00000000..ed80fef6 --- /dev/null +++ b/examples/new-api/entities/asteroid.go @@ -0,0 +1,98 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package entities + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/assets" + "gomp/examples/new-api/components" + "gomp/examples/new-api/config" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "image/color" + "math/rand" +) + +type CreateAsteroidManagers struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Velocities *stdcomponents.VelocityComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + Sprites *stdcomponents.SpriteComponentManager + AsteroidTags *components.AsteroidComponentManager + Hp *components.HpComponentManager +} + +func CreateAsteroid( + props CreateAsteroidManagers, + posX, posY, angle float32, + scaleFactor float32, + velocityX, velocityY float32, +) ecs.Entity { + bullet := props.EntityManager.Create() + props.Positions.Create(bullet, stdcomponents.Position{ + X: posX, + Y: posY, + Z: 0, + }) + props.Rotations.Create(bullet, stdcomponents.Rotation{ + Angle: angle, + }) + props.Scales.Create(bullet, stdcomponents.Scale{ + X: 1 * scaleFactor, + Y: 1 * scaleFactor, + }) + props.Velocities.Create(bullet, stdcomponents.Velocity{ + X: velocityX, + Y: velocityY, + }) + props.BoxColliders.Create(bullet, stdcomponents.BoxCollider{ + Width: 32, + Height: 32, + OffsetX: 16, + OffsetY: 16, + Layer: config.EnemyCollisionLayer, + Mask: 1 << config.PlayerCollisionLayer, + }) + props.Sprites.Create(bullet, stdcomponents.Sprite{ + Texture: assets.Textures.Get("meteor_large.png"), + Frame: rl.Rectangle{ + X: 0, + Y: 0, + Width: 64, + Height: 64, + }, + Origin: rl.Vector2{ + X: 32, + Y: 32, + }, + Tint: color.RGBA{ + R: 255, + G: 255, + B: 255, + A: 255, + }, + }) + props.AsteroidTags.Create(bullet, components.AsteroidTag{}) + hp := int32(3 + rand.Intn(6)) + props.Hp.Create(bullet, components.Hp{ + Hp: hp, + MaxHp: hp, + }) + + return bullet +} diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go new file mode 100644 index 00000000..edf06a41 --- /dev/null +++ b/examples/new-api/entities/bullet.go @@ -0,0 +1,95 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package entities + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/assets" + "gomp/examples/new-api/components" + "gomp/examples/new-api/config" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "image/color" +) + +type CreateBulletManagers struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Velocities *stdcomponents.VelocityComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + Sprites *stdcomponents.SpriteComponentManager + BulletTags *components.BulletTagComponentManager + Hps *components.HpComponentManager +} + +func CreateBullet( + props CreateBulletManagers, + posX, posY, angle float32, + velocityX, velocityY float32, +) ecs.Entity { + bullet := props.EntityManager.Create() + props.Positions.Create(bullet, stdcomponents.Position{ + X: posX, + Y: posY, + Z: 0, + }) + props.Rotations.Create(bullet, stdcomponents.Rotation{ + Angle: angle, + }) + props.Scales.Create(bullet, stdcomponents.Scale{ + X: 1, + Y: 1, + }) + props.Velocities.Create(bullet, stdcomponents.Velocity{ + X: velocityX, + Y: velocityY, + }) + props.BoxColliders.Create(bullet, stdcomponents.BoxCollider{ + Width: 16, + Height: 16, + OffsetX: 8, + OffsetY: 8, + Layer: config.PlayerCollisionLayer, + Mask: 1 << config.EnemyCollisionLayer, + }) + props.Sprites.Create(bullet, stdcomponents.Sprite{ + Texture: assets.Textures.Get("bullet.png"), + Frame: rl.Rectangle{ + X: 0, + Y: 0, + Width: 64, + Height: 64, + }, + Origin: rl.Vector2{ + X: 32, + Y: 32, + }, + Tint: color.RGBA{ + R: 255, + G: 255, + B: 255, + A: 255, + }, + }) + props.BulletTags.Create(bullet, components.BulletTag{}) + props.Hps.Create(bullet, components.Hp{ + Hp: 1, + MaxHp: 1, + }) + + return bullet +} diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index 7360f870..05ff1297 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -37,7 +37,7 @@ type Player struct { Renderable *stdcomponents.Renderable YSort *stdcomponents.YSort RenderOrder *stdcomponents.RenderOrder - ColliderBox *stdcomponents.ColliderBox + ColliderBox *stdcomponents.BoxCollider GenericCollider *stdcomponents.GenericCollider } @@ -55,7 +55,7 @@ func CreatePlayer( renderables *stdcomponents.RenderableComponentManager, ySorts *stdcomponents.YSortComponentManager, renderOrders *stdcomponents.RenderOrderComponentManager, - boxColliders *stdcomponents.ColliderBoxComponentManager, + boxColliders *stdcomponents.BoxColliderComponentManager, genericColliders *stdcomponents.GenericColliderComponentManager, ) (player Player) { // Creating new player @@ -108,8 +108,8 @@ func CreatePlayer( // Adding RenderOrder component player.RenderOrder = renderOrders.Create(entity, stdcomponents.RenderOrder{}) - // Adding ColliderBox component - player.ColliderBox = boxColliders.Create(entity, stdcomponents.ColliderBox{ + // Adding BoxCollider component + player.ColliderBox = boxColliders.Create(entity, stdcomponents.BoxCollider{ Width: 96, Height: 128, }) diff --git a/examples/new-api/entities/space-spawner.go b/examples/new-api/entities/space-spawner.go new file mode 100644 index 00000000..3421d374 --- /dev/null +++ b/examples/new-api/entities/space-spawner.go @@ -0,0 +1,52 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package entities + +import ( + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +type CreateSpaceSpawnerManagers struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Velocities *stdcomponents.VelocityComponentManager + SpaceSpawners *components.SpaceSpawnerComponentManager +} + +func CreateSpaceSpawner( + props CreateSpaceSpawnerManagers, + posX, posY float32, + velX float32, + spawnRate time.Duration, +) ecs.Entity { + e := props.EntityManager.Create() + props.Positions.Create(e, stdcomponents.Position{ + X: posX, + Y: posY, + }) + props.Velocities.Create(e, stdcomponents.Velocity{ + X: velX, + Y: 0, + }) + props.SpaceSpawners.Create(e, components.SpaceSpawnerTag{ + Cooldown: spawnRate, + CooldownLeft: 0, + }) + + return e +} diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go new file mode 100644 index 00000000..cea363cf --- /dev/null +++ b/examples/new-api/entities/spaceship.go @@ -0,0 +1,100 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package entities + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/assets" + "gomp/examples/new-api/components" + "gomp/examples/new-api/config" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "image/color" + "time" +) + +type CreateSpaceShipManagers struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Velocities *stdcomponents.VelocityComponentManager + Sprites *stdcomponents.SpriteComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + + PlayerTags *components.PlayerTagComponentManager + Hps *components.HpComponentManager + Weapons *components.WeaponComponentManager + SpaceshipIntents *components.SpaceshipIntentComponentManager +} + +func CreateSpaceShip( + props CreateSpaceShipManagers, + posX, posY float32, + rotationAngle float32, +) ecs.Entity { + spaceShip := props.EntityManager.Create() + props.Positions.Create(spaceShip, stdcomponents.Position{ + X: posX, + Y: posY, + Z: 0, + }) + + props.Rotations.Create(spaceShip, stdcomponents.Rotation{ + Angle: rotationAngle, + }) + + props.Scales.Create(spaceShip, stdcomponents.Scale{ + X: 1, + Y: 1, + }) + + props.Velocities.Create(spaceShip, stdcomponents.Velocity{ + X: 0, + Y: 0, + }) + + props.Sprites.Create(spaceShip, stdcomponents.Sprite{ + Texture: assets.Textures.Get("ship_E.png"), + Origin: rl.Vector2{X: 32, Y: 40}, + Frame: rl.Rectangle{0, 0, 64, 64}, + Tint: color.RGBA{255, 255, 255, 255}, + }) + + props.BoxColliders.Create(spaceShip, stdcomponents.BoxCollider{ + Width: 32, + Height: 32, + OffsetX: 16, + OffsetY: 16, + Layer: config.PlayerCollisionLayer, + Mask: 1< 5000 { + s.EntityManager.Delete(e) + } + + if hp.Hp <= 0 { + s.EntityManager.Delete(e) + } + + return true + }) + + s.BulletTags.EachEntity(func(entity ecs.Entity) bool { + pos := s.Positions.Get(entity) + if pos.Y > 5000 || pos.Y < 0 || pos.X > 5000 || pos.X < 0 { + s.EntityManager.Delete(entity) + } + return true + }) + s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { - if collision.State == stdcomponents.CollisionStateEnter { - log.Println("Collision entered") - e1Tag := s.Players.Get(collision.E1) - e2Tag := s.Players.Get(collision.E2) - - if e1Tag != nil { - log.Println("PlayerTag 1 collided with player 2") - s.EntityManager.Delete(collision.E2) + switch collision.State { + case stdcomponents.CollisionStateEnter: + if s.checkBulletCollisionEnter(collision.E1, collision.E2) { + return true } - if e2Tag != nil { - log.Println("PlayerTag 2 collided with player 1") - s.EntityManager.Delete(collision.E1) + if s.checkPlayerCollisionEnter(collision.E1, collision.E2) { + return true } - } + case stdcomponents.CollisionStateExit: + default: - if collision.State == stdcomponents.CollisionStateExit { - log.Println("Collision exited") } return true }) } func (s *CollisionHandlerSystem) Destroy() {} + +func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bool { + e1Tag := s.PlayerTags.Get(e1) + e2Tag := s.PlayerTags.Get(e2) + + if e1Tag != nil { + // this is a player + asteroidTag := s.AsteroidTags.Get(e2) + if asteroidTag != nil { + hp := s.Hps.Get(e1) + hp.Hp -= 1 + return true + } + + wallTag := s.WallTags.Get(e2) + if wallTag != nil { + // reverse player movement vector + velocity := s.Velocities.Get(e1) + rotation := s.Rotations.Get(e1) + velocity.X *= -1 + velocity.Y *= -1 + rotation.Angle += 180 + return true + } + } else if e2Tag != nil { + // this is a player + asteroidTag := s.AsteroidTags.Get(e1) + if asteroidTag != nil { + hp := s.Hps.Get(e2) + hp.Hp -= 1 + return true + } + + wallTag := s.WallTags.Get(e1) + if wallTag != nil { + // reverse player movement vector + velocity := s.Velocities.Get(e2) + rotation := s.Rotations.Get(e2) + velocity.X *= -1 + velocity.Y *= -1 + rotation.Angle += 180 + return true + } + } + + return false +} + +func (s *CollisionHandlerSystem) checkBulletCollisionEnter(e1, e2 ecs.Entity) bool { + e1Tag := s.BulletTags.Get(e1) + e2Tag := s.BulletTags.Get(e2) + + if e1Tag != nil { + // this is a bullet + bulletHp := s.Hps.Get(e1) + asteroidTag := s.AsteroidTags.Get(e2) + if asteroidTag != nil { + asteroidHp := s.Hps.Get(e2) + asteroidHp.Hp -= 1 + bulletHp.Hp -= 1 + return true + } + } else if e2Tag != nil { + // this is a bullet + bulletHp := s.Hps.Get(e2) + asteroidTag := s.AsteroidTags.Get(e1) + if asteroidTag != nil { + asteroidHp := s.Hps.Get(e1) + asteroidHp.Hp -= 1 + bulletHp.Hp -= 1 + return true + } + } + + return false +} diff --git a/examples/new-api/systems/hp.go b/examples/new-api/systems/hp.go new file mode 100644 index 00000000..d6034466 --- /dev/null +++ b/examples/new-api/systems/hp.go @@ -0,0 +1,60 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package systems + +import ( + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "time" +) + +func NewHpSystem() HpSystem { + return HpSystem{} +} + +type HpSystem struct { + EntityManager *ecs.EntityManager + Hps *components.HpComponentManager + Asteroids *components.AsteroidComponentManager + Players *components.PlayerTagComponentManager + Hp *components.HpComponentManager + AsteroidSceneManager *components.AsteroidSceneManagerComponentManager +} + +func (s *HpSystem) Init() {} +func (s *HpSystem) Run(dt time.Duration) { + s.Hps.EachEntity(func(e ecs.Entity) bool { + hp := s.Hps.Get(e) + + if hp.Hp <= 0 { + asteroid := s.Asteroids.Get(e) + player := s.Players.Get(e) + s.AsteroidSceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { + if asteroid != nil { + a.PlayerScore += hp.MaxHp + } + if player != nil { + playerHp := s.Hp.Get(e) + a.PlayerHp = playerHp.Hp + } + return false + }) + + s.EntityManager.Delete(e) + } + return true + }) +} +func (s *HpSystem) Destroy() {} diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 9a472a2a..60e26878 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -32,12 +32,12 @@ type PlayerSystem struct { AnimationStates *stdcomponents.AnimationStateComponentManager Tints *stdcomponents.TintComponentManager Flips *stdcomponents.FlipComponentManager - HP *components.HealthComponentManager + HP *components.HpComponentManager Controllers *components.ControllerComponentManager Renderables *stdcomponents.RenderableComponentManager YSorts *stdcomponents.YSortComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager - BoxColliders *stdcomponents.ColliderBoxComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager GenericCollider *stdcomponents.GenericColliderComponentManager Players *components.PlayerTagComponentManager } diff --git a/examples/new-api/systems/render-assterodd.go b/examples/new-api/systems/render-assterodd.go new file mode 100644 index 00000000..935b7565 --- /dev/null +++ b/examples/new-api/systems/render-assterodd.go @@ -0,0 +1,313 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package systems + +import ( + "fmt" + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "math" + "slices" + "sync" + "time" +) + +func NewRenderAssteroddSystem() RenderAssteroddSystem { + return RenderAssteroddSystem{} +} + +type RenderAssteroddSystem struct { + EntityManager *ecs.EntityManager + RlTexturePros *stdcomponents.RLTextureProComponentManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + Tints *stdcomponents.TintComponentManager + Flips *stdcomponents.FlipComponentManager + Renderables *stdcomponents.RenderableComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + Sprites *stdcomponents.SpriteComponentManager + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + RenderOrders *stdcomponents.RenderOrderComponentManager + ColliderBoxes *stdcomponents.BoxColliderComponentManager + Collisions *stdcomponents.CollisionComponentManager + renderList []renderEntry + instanceData []stdcomponents.RLTexturePro + camera rl.Camera2D + SceneManager *components.AsteroidSceneManagerComponentManager + + monitorWidth int + monitorHeight int + + Player *components.PlayerTagComponentManager +} + +func (s *RenderAssteroddSystem) Init() { + s.monitorWidth = rl.GetScreenWidth() + s.monitorHeight = rl.GetScreenHeight() + s.camera = rl.Camera2D{ + Target: rl.NewVector2(float32(s.monitorWidth/2), float32(s.monitorHeight/2)), + Offset: rl.NewVector2(float32(s.monitorWidth/2), float32(s.monitorHeight/2)), + Rotation: 0, + Zoom: 1, + } +} +func (s *RenderAssteroddSystem) Run(dt time.Duration) bool { + if rl.WindowShouldClose() { + return false + } + + s.prepareRender(dt) + + rl.BeginDrawing() + rl.ClearBackground(rl.Black) + s.render() + + rl.DrawFPS(10, 10) + rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) + s.SceneManager.EachComponent(func(a *components.AsteroidSceneManager) bool { + rl.DrawText(fmt.Sprintf("Player HP: %d", a.PlayerHp), 10, 50, 20, rl.RayWhite) + rl.DrawText(fmt.Sprintf("Score: %d", a.PlayerScore), 10, 70, 20, rl.RayWhite) + if a.PlayerHp <= 0 { + text := "Game Over" + textSize := rl.MeasureTextEx(rl.GetFontDefault(), text, 96, 0) + x := (s.monitorWidth - int(textSize.X)) / 2 + y := (s.monitorHeight - int(textSize.Y)) / 2 + rl.DrawText(text, int32(x), int32(y), 96, rl.Red) + + } + return false + }) + + rl.EndDrawing() + + return true +} + +func (s *RenderAssteroddSystem) Destroy() {} + +func (s *RenderAssteroddSystem) render() { + + // Extract and sort entities + if cap(s.renderList) < s.Renderables.Len() { + s.renderList = append(s.renderList, make([]renderEntry, 0, s.Renderables.Len()-cap(s.renderList))...) + } + s.Renderables.EachEntity(func(e ecs.Entity) bool { + renderOrder := s.RenderOrders.Get(e) + + spriteMatrix := s.SpriteMatrixes.Get(e) + if spriteMatrix != nil { + s.renderList = append(s.renderList, renderEntry{ + Entity: e, + TextureId: int(spriteMatrix.Texture.ID), + ZIndex: renderOrder.CalculatedZ, + }) + return true + } + + sprite := s.Sprites.Get(e) + if sprite != nil { + s.renderList = append(s.renderList, renderEntry{ + Entity: e, + TextureId: int(sprite.Texture.ID), + ZIndex: renderOrder.CalculatedZ, + }) + return true + } + + panic("Unknown renderable type") + }) + + slices.SortStableFunc(s.renderList, func(a, b renderEntry) int { + if a.TextureId == b.TextureId { + return int(math.Floor(float64(a.ZIndex - b.ZIndex))) + } + return int(a.TextureId - b.TextureId) + }) + + // Batch and render + var currentTex = -1 + var instanceData []stdcomponents.RLTexturePro = make([]stdcomponents.RLTexturePro, 0, 8192) + for i := range s.renderList { + entry := &s.renderList[i] + if entry.TextureId != currentTex || len(instanceData) >= 8192 { + if len(instanceData) > 0 { + s.submitBatch(currentTex, instanceData) + instanceData = instanceData[:0] + } + currentTex = entry.TextureId + } + instanceData = append(instanceData, s.getInstanceData(entry.Entity)) + } + s.submitBatch(currentTex, instanceData) // Submit last batch + s.renderList = s.renderList[:0] + + rl.BeginMode2D(s.camera) + s.Collisions.EachEntity(func(entity ecs.Entity) bool { + pos := s.Positions.Get(entity) + rl.DrawRectangle(int32(pos.X), int32(pos.Y), 16, 16, rl.Red) + return true + }) + //s.ColliderBoxes.EachEntity(func(e ecs.Entity) bool { + // box := s.ColliderBoxes.Get(e) + // pos := s.Positions.Get(e) + // scale := s.Scales.Get(e) + // col := s.ColliderBoxes.Get(e) + // + // rl.DrawRectangleLines(int32(pos.X-(col.OffsetX*scale.X)), int32(pos.Y-(col.OffsetY*scale.Y)), int32(box.Width*scale.X), int32(box.Height*scale.Y), rl.DarkGreen) + // return true + //}) + //s.Renderables.EachEntity(func(e ecs.Entity) bool { + // position := s.Positions.Get(e) + // rl.DrawRectangle(int32(position.X-2), int32(position.Y-2), 4, 4, rl.Red) + // return true + //}) + rl.EndMode2D() +} + +func (s *RenderAssteroddSystem) submitBatch(texID int, data []stdcomponents.RLTexturePro) { + rl.BeginMode2D(s.camera) + for i := range data { + rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) + } + rl.EndMode2D() +} + +func (s *RenderAssteroddSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { + return *s.RlTexturePros.Get(e) +} + +func (s *RenderAssteroddSystem) prepareRender(dt time.Duration) { + wg := new(sync.WaitGroup) + wg.Add(6) + s.prepareAnimations(wg) + go s.prepareFlips(wg) + go s.preparePositions(wg, dt) + go s.prepareRotations(wg) + go s.prepareScales(wg) + go s.prepareTints(wg) + wg.Wait() +} + +func (s *RenderAssteroddSystem) prepareAnimations(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + animation := s.AnimationPlayers.Get(entity) + if animation == nil { + return true + } + frame := &texturePro.Frame + if animation.Vertical { + frame.Y += frame.Height * float32(animation.Current) + } else { + frame.X += frame.Width * float32(animation.Current) + } + return true + }) +} + +func (s *RenderAssteroddSystem) prepareFlips(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + mirrored := s.Flips.Get(entity) + if mirrored == nil { + return true + } + if mirrored.X { + texturePro.Frame.Width *= -1 + } + if mirrored.Y { + texturePro.Frame.Height *= -1 + } + return true + }) +} + +func (s *RenderAssteroddSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { + defer wg.Done() + dts := dt.Seconds() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + position := s.Positions.Get(entity) + if position == nil { + return true + } + decay := 40.0 // DECAY IS TICKRATE DEPENDENT + x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.X), decay, dts)) + y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.Y), decay, dts)) + texturePro.Dest.X = x + texturePro.Dest.Y = y + player := s.Player.Get(entity) + if player != nil { + s.camera.Target.X = x + s.camera.Target.Y = y + } + + return true + }) +} + +func (s *RenderAssteroddSystem) prepareRotations(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + rotation := s.Rotations.Get(entity) + if rotation == nil { + return true + } + texturePro.Rotation = rotation.Angle + return true + }) +} + +func (s *RenderAssteroddSystem) prepareScales(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + texturePro := s.RlTexturePros.Get(entity) + scale := s.Scales.Get(entity) + if scale == nil { + return true + } + texturePro.Dest.Width *= scale.X + texturePro.Dest.Height *= scale.Y + return true + }) +} + +func (s *RenderAssteroddSystem) prepareTints(wg *sync.WaitGroup) { + defer wg.Done() + s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { + tr := s.RlTexturePros.Get(entity) + tint := s.Tints.Get(entity) + if tint == nil { + return true + } + trTint := &tr.Tint + trTint.A = tint.A + trTint.R = tint.R + trTint.G = tint.G + trTint.B = tint.B + return true + }) +} + +func (s *RenderAssteroddSystem) expDecay(a, b, decay, dt float64) float64 { + return b + (a-b)*(math.Exp(-decay*dt)) +} diff --git a/stdsystems/render.go b/examples/new-api/systems/render-bogdan.go similarity index 82% rename from stdsystems/render.go rename to examples/new-api/systems/render-bogdan.go index 284314e6..bd677652 100644 --- a/stdsystems/render.go +++ b/examples/new-api/systems/render-bogdan.go @@ -12,7 +12,7 @@ none :) Thank you for your support! */ -package stdsystems +package systems import ( "fmt" @@ -25,11 +25,11 @@ import ( "time" ) -func NewRenderSystem() RenderSystem { - return RenderSystem{} +func NewRenderBogdanSystem() RenderBogdanSystem { + return RenderBogdanSystem{} } -type RenderSystem struct { +type RenderBogdanSystem struct { EntityManager *ecs.EntityManager RlTexturePros *stdcomponents.RLTextureProComponentManager Positions *stdcomponents.PositionComponentManager @@ -42,23 +42,20 @@ type RenderSystem struct { AnimationStates *stdcomponents.AnimationStateComponentManager SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager - ColliderBoxes *stdcomponents.ColliderBoxComponentManager + ColliderBoxes *stdcomponents.BoxColliderComponentManager Collisions *stdcomponents.CollisionComponentManager - renderList []RenderEntry + renderList []renderEntry instanceData []stdcomponents.RLTexturePro camera rl.Camera2D } -type RenderEntry struct { +type renderEntry struct { Entity ecs.Entity TextureId int ZIndex float32 } -func (s *RenderSystem) Init() { - rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") - //InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") - +func (s *RenderBogdanSystem) Init() { s.camera = rl.Camera2D{ Target: rl.NewVector2(0, 0), Offset: rl.NewVector2(0, 0), @@ -66,7 +63,7 @@ func (s *RenderSystem) Init() { Zoom: 1, } } -func (s *RenderSystem) Run(dt time.Duration) bool { +func (s *RenderBogdanSystem) Run(dt time.Duration) bool { if rl.WindowShouldClose() { return false } @@ -104,19 +101,17 @@ func (s *RenderSystem) Run(dt time.Duration) bool { return true } -func (s *RenderSystem) Destroy() { - rl.CloseWindow() -} +func (s *RenderBogdanSystem) Destroy() {} -func (s *RenderSystem) render() { +func (s *RenderBogdanSystem) render() { // Extract and sort entities if cap(s.renderList) < s.Renderables.Len() { - s.renderList = append(s.renderList, make([]RenderEntry, 0, s.Renderables.Len()-cap(s.renderList))...) + s.renderList = append(s.renderList, make([]renderEntry, 0, s.Renderables.Len()-cap(s.renderList))...) } s.Renderables.EachEntity(func(e ecs.Entity) bool { sprite := s.SpriteMatrixes.Get(e) renderOrder := s.RenderOrders.Get(e) - s.renderList = append(s.renderList, RenderEntry{ + s.renderList = append(s.renderList, renderEntry{ Entity: e, TextureId: int(sprite.Texture.ID), ZIndex: renderOrder.CalculatedZ, @@ -124,7 +119,7 @@ func (s *RenderSystem) render() { return true }) - slices.SortStableFunc(s.renderList, func(a, b RenderEntry) int { + slices.SortStableFunc(s.renderList, func(a, b renderEntry) int { if a.TextureId == b.TextureId { return int(math.Floor(float64(a.ZIndex - b.ZIndex))) } @@ -149,11 +144,11 @@ func (s *RenderSystem) render() { s.renderList = s.renderList[:0] } -func (s *RenderSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { +func (s *RenderBogdanSystem) getInstanceData(e ecs.Entity) stdcomponents.RLTexturePro { return *s.RlTexturePros.Get(e) } -func (s *RenderSystem) prepareRender(dt time.Duration) { +func (s *RenderBogdanSystem) prepareRender(dt time.Duration) { wg := new(sync.WaitGroup) wg.Add(6) s.prepareAnimations(wg) @@ -165,7 +160,7 @@ func (s *RenderSystem) prepareRender(dt time.Duration) { wg.Wait() } -func (s *RenderSystem) prepareAnimations(wg *sync.WaitGroup) { +func (s *RenderBogdanSystem) prepareAnimations(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) @@ -183,7 +178,7 @@ func (s *RenderSystem) prepareAnimations(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { +func (s *RenderBogdanSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) @@ -201,7 +196,7 @@ func (s *RenderSystem) prepareFlips(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { +func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { defer wg.Done() //dts := dt.Seconds() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { @@ -220,7 +215,7 @@ func (s *RenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { }) } -func (s *RenderSystem) prepareRotations(wg *sync.WaitGroup) { +func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) @@ -233,7 +228,7 @@ func (s *RenderSystem) prepareRotations(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) prepareScales(wg *sync.WaitGroup) { +func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) @@ -247,7 +242,7 @@ func (s *RenderSystem) prepareScales(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) prepareTints(wg *sync.WaitGroup) { +func (s *RenderBogdanSystem) prepareTints(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { tr := s.RlTexturePros.Get(entity) @@ -264,7 +259,7 @@ func (s *RenderSystem) prepareTints(wg *sync.WaitGroup) { }) } -func (s *RenderSystem) submitBatch(texID int, data []stdcomponents.RLTexturePro) { +func (s *RenderBogdanSystem) submitBatch(texID int, data []stdcomponents.RLTexturePro) { rl.BeginMode2D(s.camera) for i := range data { rl.DrawTexturePro(*data[i].Texture, data[i].Frame, data[i].Dest, data[i].Origin, data[i].Rotation, data[i].Tint) diff --git a/examples/new-api/systems/space-spawner.go b/examples/new-api/systems/space-spawner.go new file mode 100644 index 00000000..005f144e --- /dev/null +++ b/examples/new-api/systems/space-spawner.go @@ -0,0 +1,75 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package systems + +import ( + "gomp/examples/new-api/components" + "gomp/examples/new-api/entities" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "math/rand" + "time" +) + +func NewSpaceSpawnerSystem() SpaceSpawnerSystem { + return SpaceSpawnerSystem{} +} + +type SpaceSpawnerSystem struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + SpaceSpawners *components.SpaceSpawnerComponentManager + Asteroids *components.AsteroidComponentManager + Hp *components.HpComponentManager + Sprites *stdcomponents.SpriteComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + Velocities *stdcomponents.VelocityComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager +} + +func (s *SpaceSpawnerSystem) Init() {} +func (s *SpaceSpawnerSystem) Run(dt time.Duration) { + s.SpaceSpawners.EachEntity(func(e ecs.Entity) bool { + position := s.Positions.Get(e) + velocity := s.Velocities.Get(e) + + if position.X > 5000 || position.X < 0 { + velocity.X = -velocity.X + } + + spawner := s.SpaceSpawners.Get(e) + if spawner.CooldownLeft > 0 { + spawner.CooldownLeft -= dt + return true + } + + pos := s.Positions.Get(e) + entities.CreateAsteroid(entities.CreateAsteroidManagers{ + EntityManager: s.EntityManager, + Positions: s.Positions, + Rotations: s.Rotations, + Scales: s.Scales, + Velocities: s.Velocities, + BoxColliders: s.BoxColliders, + Sprites: s.Sprites, + AsteroidTags: s.Asteroids, + Hp: s.Hp, + }, pos.X, pos.Y, 0, 1+rand.Float32()*2, 0, 50+rand.Float32()*100) + spawner.CooldownLeft = spawner.Cooldown + return true + }) +} +func (s *SpaceSpawnerSystem) Destroy() {} diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go new file mode 100644 index 00000000..ceb553da --- /dev/null +++ b/examples/new-api/systems/spaceship-intents.go @@ -0,0 +1,129 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package systems + +import ( + "gomp/examples/new-api/components" + "gomp/examples/new-api/entities" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "math" + "time" +) + +func NewSpaceshipIntentsSystem() SpaceshipIntentsSystem { + return SpaceshipIntentsSystem{} +} + +type SpaceshipIntentsSystem struct { + EntityManager *ecs.EntityManager + SpaceshipIntents *components.SpaceshipIntentComponentManager + Positions *stdcomponents.PositionComponentManager + Velocities *stdcomponents.VelocityComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + BulletTags *components.BulletTagComponentManager + Sprites *stdcomponents.SpriteComponentManager + Weapons *components.WeaponComponentManager + Hps *components.HpComponentManager + moveSpeed float32 +} + +func (s *SpaceshipIntentsSystem) Init() {} +func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { + var moveSpeedMax float32 = 300 + var moveSpeedMaxBackwards float32 = -200 + var rotateSpeed float32 = 10 + var speedIncrement float32 = 10 + + var bulletSpeed float32 = 300 + + s.SpaceshipIntents.EachEntity(func(entity ecs.Entity) bool { + intent := s.SpaceshipIntents.Get(entity) + vel := s.Velocities.Get(entity) + rot := s.Rotations.Get(entity) + pos := s.Positions.Get(entity) + weapon := s.Weapons.Get(entity) + + if pos.Y < 0 || pos.Y > 5000 || pos.X < 0 || pos.X > 5000 { + vel.X *= -1 + vel.Y *= -1 + rot.Angle += 180 + } + + if intent.RotateLeft { + rot.Angle -= rotateSpeed + } + if intent.RotateRight { + rot.Angle += rotateSpeed + } + if intent.MoveUp { + s.moveSpeed += speedIncrement + if s.moveSpeed > moveSpeedMax { + s.moveSpeed = moveSpeedMax + } + } + if intent.MoveDown { + s.moveSpeed -= speedIncrement + if s.moveSpeed < moveSpeedMaxBackwards { + s.moveSpeed = moveSpeedMaxBackwards + } + } + + if !intent.MoveUp && !intent.MoveDown { + if s.moveSpeed > 0 { + s.moveSpeed -= speedIncrement + } else if s.moveSpeed < 0 { + s.moveSpeed += speedIncrement + } + } + + rads := deg2rad(float64(rot.Angle)) + math.Pi + + vel.Y = float32(math.Cos(rads)) * s.moveSpeed + vel.X = -float32(math.Sin(rads)) * s.moveSpeed + + bulletVelocityY := vel.Y + float32(math.Cos(rads))*bulletSpeed + bulletVelocityX := vel.X - float32(math.Sin(rads))*bulletSpeed + + if weapon.CooldownLeft <= 0 { + if intent.Fire { + entities.CreateBullet(entities.CreateBulletManagers{ + EntityManager: s.EntityManager, + Positions: s.Positions, + Rotations: s.Rotations, + Scales: s.Scales, + Velocities: s.Velocities, + BoxColliders: s.BoxColliders, + Sprites: s.Sprites, + BulletTags: s.BulletTags, + Hps: s.Hps, + }, pos.X, pos.Y, rot.Angle, bulletVelocityX, bulletVelocityY) + weapon.CooldownLeft = weapon.Cooldown + } + } else { + weapon.CooldownLeft -= dt + } + + return true + }) +} + +func (s *SpaceshipIntentsSystem) Destroy() {} + +func deg2rad(deg float64) float64 { + return deg * math.Pi / 180 +} diff --git a/game.go b/game.go index 8104fe07..ee2119c2 100644 --- a/game.go +++ b/game.go @@ -42,7 +42,8 @@ func NewGame(scenes ...AnyScene) Game { } game := Game{ - Scenes: sceneSet, + Scenes: sceneSet, + RenderSystem: NewRenderSystem(), } return game @@ -53,6 +54,7 @@ type Game struct { CurrentSceneId SceneId shouldDestroy bool + RenderSystem RenderSystem } func (g *Game) Init() { @@ -62,6 +64,7 @@ func (g *Game) Init() { scene, ok := g.Scenes[g.CurrentSceneId] assert.True(ok, "Scene not found") + g.RenderSystem.Init() scene.Init() } @@ -89,6 +92,7 @@ func (g *Game) Destroy() { scene, ok := g.Scenes[g.CurrentSceneId] assert.True(ok, "Scene not found") scene.Destroy() + g.RenderSystem.Destroy() } func (g *Game) ShouldDestroy() bool { diff --git a/internal/sdl2/sdl-game.go b/internal/sdl2/sdl-game.go index 8c0cb157..2a37e1ff 100644 --- a/internal/sdl2/sdl-game.go +++ b/internal/sdl2/sdl-game.go @@ -14,7 +14,7 @@ const ( fontSize = 32 ) -var winTitle string = "Go-SDL2 Render" +var winTitle string = "Go-SDL2 RenderAssterodd" var winWidth, winHeight int32 = 800, 600 func run() int { diff --git a/internal/tomb-mates-demo/main.go b/internal/tomb-mates-demo/main.go index 27930efe..f7c61e9b 100644 --- a/internal/tomb-mates-demo/main.go +++ b/internal/tomb-mates-demo/main.go @@ -172,7 +172,7 @@ package tomb_mates_demo // // } // g.Mx.Lock() -// components.Render.Each(g.EntityManager, func(e *ecs.Entry) { +// components.RenderAssterodd.Each(g.EntityManager, func(e *ecs.Entry) { // body := components.Transform.GetValue(e) // op.GeoM.Reset() diff --git a/render.go b/render.go new file mode 100644 index 00000000..2d4b400e --- /dev/null +++ b/render.go @@ -0,0 +1,40 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "time" +) + +func NewRenderSystem() RenderSystem { + return RenderSystem{} +} + +type RenderSystem struct{} + +func (s *RenderSystem) Init() { + rl.InitWindow(0, 0, "raylib [core] ebiten-ecs - basic window") +} +func (s *RenderSystem) Run(dt time.Duration) bool { + if rl.WindowShouldClose() { + return false + } + return true +} + +func (s *RenderSystem) Destroy() { + rl.CloseWindow() +} diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 5d305561..d4461c8e 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -32,31 +32,39 @@ const ( ColliderLayerNone CollisionLayer = 0 ) -type ColliderBox struct { - Width float32 - Height float32 +type BoxCollider struct { + Width float32 + Height float32 + OffsetX float32 + OffsetY float32 + Layer CollisionLayer + Mask CollisionMask } -type ColliderBoxComponentManager = ecs.ComponentManager[ColliderBox] +type BoxColliderComponentManager = ecs.ComponentManager[BoxCollider] -func NewColliderBoxComponentManager() ColliderBoxComponentManager { - return ecs.NewComponentManager[ColliderBox](ColliderBoxComponentId) +func NewBoxColliderComponentManager() BoxColliderComponentManager { + return ecs.NewComponentManager[BoxCollider](ColliderBoxComponentId) } type ColliderCircle struct { Radius float32 + Layer CollisionLayer + Mask CollisionMask } -type ColliderCircleComponentManager = ecs.ComponentManager[ColliderCircle] +type CircleColliderComponentManager = ecs.ComponentManager[ColliderCircle] -func NewColliderCircleComponentManager() ColliderCircleComponentManager { +func NewColliderCircleComponentManager() CircleColliderComponentManager { return ecs.NewComponentManager[ColliderCircle](ColliderCircleComponentId) } type GenericCollider struct { - Shape ColliderShape - Layer CollisionLayer - Mask CollisionMask + Shape ColliderShape + Layer CollisionLayer + Mask CollisionMask + OffsetX float32 + OffsetY float32 } type GenericColliderComponentManager = ecs.ComponentManager[GenericCollider] diff --git a/stdcomponents/renderable.go b/stdcomponents/renderable.go index 92fada06..0c84dcc7 100644 --- a/stdcomponents/renderable.go +++ b/stdcomponents/renderable.go @@ -18,6 +18,7 @@ import "gomp/pkg/ecs" const ( InvalidRenderableType Renderable = iota + SpriteRenderableType SpriteMatrixRenderableType ) diff --git a/stdsystems/collider.go b/stdsystems/collider.go new file mode 100644 index 00000000..c71db588 --- /dev/null +++ b/stdsystems/collider.go @@ -0,0 +1,68 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewColliderSystem() ColliderSystem { + return ColliderSystem{} +} + +type ColliderSystem struct { + EntityManager *ecs.EntityManager + GenericColliders *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager +} + +func (s *ColliderSystem) Init() {} +func (s *ColliderSystem) Run(dt time.Duration) { + s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { + boxCollider := s.BoxColliders.Get(entity) + + genCollider := s.GenericColliders.Get(entity) + if genCollider == nil { + s.GenericColliders.Create(entity, stdcomponents.GenericCollider{ + Shape: stdcomponents.BoxColliderShape, + Layer: boxCollider.Layer, + Mask: boxCollider.Mask, + OffsetX: boxCollider.OffsetX, + OffsetY: boxCollider.OffsetY, + }) + } + + return true + }) + + s.CircleColliders.EachEntity(func(entity ecs.Entity) bool { + circleCollider := s.CircleColliders.Get(entity) + + genCollider := s.GenericColliders.Get(entity) + if genCollider == nil { + s.GenericColliders.Create(entity, stdcomponents.GenericCollider{ + Shape: stdcomponents.CircleColliderShape, + Layer: circleCollider.Layer, + Mask: circleCollider.Mask, + }) + } + + return true + }) +} +func (s *ColliderSystem) Destroy() {} diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 353a6777..ff4831f7 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -24,8 +24,8 @@ import ( func NewCollisionDetectionSystem() CollisionDetectionSystem { return CollisionDetectionSystem{ - cellSizeX: 96, - cellSizeY: 128, + cellSizeX: 192, + cellSizeY: 192, spatialBuckets: make(map[stdcomponents.SpatialIndex][]ecs.Entity, 32), } } @@ -33,8 +33,9 @@ func NewCollisionDetectionSystem() CollisionDetectionSystem { type CollisionDetectionSystem struct { EntityManager *ecs.EntityManager Positions *stdcomponents.PositionComponentManager + Scales *stdcomponents.ScaleComponentManager GenericCollider *stdcomponents.GenericColliderComponentManager - BoxColliders *stdcomponents.ColliderBoxComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager Collisions *stdcomponents.CollisionComponentManager SpatialIndex *stdcomponents.SpatialIndexComponentManager @@ -75,8 +76,11 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { // Build spatial buckets and entity-to-cell map s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { position := s.Positions.Get(entity) - cellX := int(position.X) / s.cellSizeX - cellY := int(position.Y) / s.cellSizeY + scale := s.Scales.Get(entity) + + collider := s.GenericCollider.Get(entity) + cellX := int(position.X-(collider.OffsetX*scale.X)) / s.cellSizeX + cellY := int(position.Y-(collider.OffsetY*scale.Y)) / s.cellSizeY cell := stdcomponents.SpatialIndex{X: cellX, Y: cellY} s.entityToCell[entity] = cell s.spatialBuckets[cell] = append(s.spatialBuckets[cell], entity) @@ -88,12 +92,14 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { position := s.Positions.Get(entity) collider := s.BoxColliders.Get(entity) - s.aabbs[entity] = AABB{ - Left: position.X, - Right: position.X + collider.Width, - Top: position.Y, - Bottom: position.Y + collider.Height, + scale := s.Scales.Get(entity) + newAABB := AABB{ + Left: position.X - (collider.OffsetX * scale.X), + Right: position.X + (collider.Width-collider.OffsetX)*scale.X, + Top: position.Y - (collider.OffsetY * scale.Y), + Bottom: position.Y + (collider.Height-collider.OffsetY)*scale.Y, } + s.aabbs[entity] = newAABB return true }) diff --git a/stdsystems/collision.go b/stdsystems/collision.go index d6799391..37968e91 100644 --- a/stdsystems/collision.go +++ b/stdsystems/collision.go @@ -29,7 +29,7 @@ type CollisionSystem struct { EntityManager *ecs.EntityManager Positions *stdcomponents.PositionComponentManager GenericCollider *stdcomponents.GenericColliderComponentManager - BoxColliders *stdcomponents.ColliderBoxComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager Collisions *stdcomponents.CollisionComponentManager activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities diff --git a/stdsystems/rl-render.go b/stdsystems/rl-render.go deleted file mode 100644 index 20e94ae4..00000000 --- a/stdsystems/rl-render.go +++ /dev/null @@ -1,214 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package stdsystems - -import ( - "fmt" - rl "github.com/gen2brain/raylib-go/raylib" - "gomp/pkg/ecs" - "gomp/stdcomponents" - "math" - "sync" - "time" -) - -const ( - batchSize = 1 << 13 // Maximum batch size supported by Raylib -) - -func NewRlRenderSystem() RlRenderSystem { - return RlRenderSystem{} -} - -type RlRenderSystem struct { - EntityManager *ecs.EntityManager - RlTexturePros *stdcomponents.RLTextureProComponentManager - Positions *stdcomponents.PositionComponentManager - AnimationPlayers *stdcomponents.AnimationPlayerComponentManager - AnimationStates *stdcomponents.AnimationStateComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - Tints *stdcomponents.TintComponentManager - Flips *stdcomponents.FlipComponentManager - Renderables *stdcomponents.RenderableComponentManager - SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager - camera rl.Camera2D -} - -func (s *RlRenderSystem) Init() { - rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") - //InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") - - s.camera = rl.Camera2D{ - Target: rl.NewVector2(0, 0), - Offset: rl.NewVector2(0, 0), - Rotation: 0, - Zoom: 1, - } -} - -func (s *RlRenderSystem) Run(dt time.Duration) bool { - if rl.WindowShouldClose() { - return false - } - - s.prepareRender(dt) - - rl.BeginDrawing() - rl.ClearBackground(rl.Black) - - rl.BeginMode2D(s.camera) - s.render() - rl.EndMode2D() - - rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) - rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d entities", s.EntityManager.Size()), 10, 30, 20, rl.RayWhite) - - rl.EndDrawing() - - return true -} - -func (s *RlRenderSystem) Destroy() { - rl.CloseWindow() -} - -func (s *RlRenderSystem) render() { - s.Renderables.EachEntity(func(entity ecs.Entity) bool { - renderable := s.Renderables.Get(entity) - - switch *renderable { - case stdcomponents.SpriteMatrixRenderableType: - s.renderSpriteMatrix(entity) - default: - panic("unknown renderable type") - } - - return true - }) -} - -func (s *RlRenderSystem) prepareRender(dt time.Duration) { - wg := new(sync.WaitGroup) - wg.Add(6) - s.prepareAnimations(wg) - go s.prepareFlips(wg) - go s.preparePositions(wg, dt) - go s.prepareRotations(wg) - go s.prepareScales(wg) - go s.prepareTints(wg) - wg.Wait() -} - -func (s *RlRenderSystem) prepareAnimations(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - animation := s.AnimationPlayers.Get(entity) - if animation == nil { - return true - } - frame := &texturePro.Frame - if animation.Vertical { - frame.Y += frame.Height * float32(animation.Current) - } else { - frame.X += frame.Width * float32(animation.Current) - } - return true - }) -} - -func (s *RlRenderSystem) prepareFlips(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - mirrored := s.Flips.Get(entity) - if mirrored == nil { - return true - } - if mirrored.X { - texturePro.Frame.Width *= -1 - } - if mirrored.Y { - texturePro.Frame.Height *= -1 - } - return true - }) -} - -func (s *RlRenderSystem) preparePositions(wg *sync.WaitGroup, dt time.Duration) { - defer wg.Done() - //dts := dt.Seconds() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - position := s.Positions.Get(entity) - if position == nil { - return true - } - //decay := 16.0 // DECAY IS TICKRATE DEPENDENT - //texturePro.Dest.X = float32(s.expDecay(float64(texturePro.Dest.X), float64(position.X), decay, dts)) - //texturePro.Dest.Y = float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.Y), decay, dts)) - texturePro.Dest.X = position.X - texturePro.Dest.Y = position.Y - - return true - }) -} - -func (s *RlRenderSystem) prepareRotations(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - rotation := s.Rotations.Get(entity) - if rotation == nil { - return true - } - texturePro.Rotation = rotation.Angle - return true - }) -} - -func (s *RlRenderSystem) prepareScales(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - texturePro := s.RlTexturePros.Get(entity) - scale := s.Scales.Get(entity) - if scale == nil { - return true - } - texturePro.Dest.Width *= scale.X - texturePro.Dest.Height *= scale.Y - return true - }) -} - -func (s *RlRenderSystem) prepareTints(wg *sync.WaitGroup) { - defer wg.Done() - s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { - tr := s.RlTexturePros.Get(entity) - tint := s.Tints.Get(entity) - if tint == nil { - return true - } - trTint := &tr.Tint - trTint.A = tint.A - trTint.R = tint.R - trTint.G = tint.G - trTint.B = tint.B - return true - }) -} - -func (s *RlRenderSystem) renderSpriteMatrix(entity ecs.Entity) { - texturePro := s.RlTexturePros.Get(entity) - rl.DrawTexturePro(*texturePro.Texture, texturePro.Frame, texturePro.Dest, texturePro.Origin, texturePro.Rotation, texturePro.Tint) -} - -func (s *RlRenderSystem) expDecay(a, b, decay, dt float64) float64 { - return b + (a-b)*(math.Exp(-decay*dt)) -} diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index f906d210..99d12e51 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -15,72 +15,64 @@ Thank you for your support! package stdsystems import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" "gomp/stdcomponents" ) -func NewTextureRenderSpriteSystem() TextureRenderSpriteSystem { - return TextureRenderSpriteSystem{} +func NewSpriteSystem() SpriteSystem { + return SpriteSystem{} } -// TextureRenderSpriteSystem is a system that prepares Sprite to be rendered -type TextureRenderSpriteSystem struct { - Sprites *stdcomponents.SpriteComponentManager - TextureRenders *stdcomponents.RLTextureProComponentManager +// SpriteSystem is a system that prepares Sprite to be rendered +type SpriteSystem struct { + Positions *stdcomponents.PositionComponentManager + Scales *stdcomponents.ScaleComponentManager + Sprites *stdcomponents.SpriteComponentManager + RLTexturePros *stdcomponents.RLTextureProComponentManager + Renderables *stdcomponents.RenderableComponentManager + RenderOrder *stdcomponents.RenderOrderComponentManager } -func (s *TextureRenderSpriteSystem) Init() {} -func (s *TextureRenderSpriteSystem) Run() { - // Run sprites and spriteRenders - //s.Sprites.EachParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { - // if sprite == nil { - // return true - // } - // - // spriteFrame := sprite.Frame - // spriteOrigin := sprite.Origin - // spriteTint := sprite.Tint - // - // tr := s.RlTexturePros.Get(entity) - // if tr == nil { - // // Create new spriteRender - // newRender := stdcomponents.RLTexturePro{ - // TextureId: sprite.TextureId, - // Frame: sprite.Frame, - // Origin: sprite.Origin, - // Tint: sprite.Tint, - // Dest: rl.NewRectangle( - // 0, - // 0, - // sprite.Frame.Width, - // sprite.Frame.Height, - // ), - // } - // - // s.RlTexturePros.Create(entity, newRender) - // } else { - // // Run spriteRender - // // tr.TextureId = sprite.TextureId - // trFrame := &tr.Frame - // trFrame.X = spriteFrame.X - // trFrame.Y = spriteFrame.Y - // trFrame.Width = spriteFrame.Width - // trFrame.Height = spriteFrame.Height - // - // trOrigin := &tr.Origin - // trOrigin.X = spriteOrigin.X - // trOrigin.Y = spriteOrigin.Y - // - // trTint := &tr.Tint - // trTint.A = spriteTint.A - // trTint.R = spriteTint.R - // trTint.G = spriteTint.G - // trTint.B = spriteTint.B - // - // trDest := &tr.Dest - // trDest.Width = spriteFrame.Width - // trDest.Height = spriteFrame.Height - // } - // return true - //}) +func (s *SpriteSystem) Init() {} +func (s *SpriteSystem) Run() { + s.Sprites.EachEntityParallel(func(entity ecs.Entity) bool { + sprite := s.Sprites.Get(entity) // + position := s.Positions.Get(entity) + scale := s.Scales.Get(entity) + + renderable := s.Renderables.Get(entity) + if renderable == nil { + s.Renderables.Create(entity, stdcomponents.SpriteRenderableType) + } + + renderOrder := s.RenderOrder.Get(entity) + if renderOrder == nil { + s.RenderOrder.Create(entity, stdcomponents.RenderOrder{}) + } + + tr := s.RLTexturePros.Get(entity) + if tr == nil { + s.RLTexturePros.Create(entity, stdcomponents.RLTexturePro{ + Texture: sprite.Texture, // + Frame: sprite.Frame, // + Origin: rl.Vector2{ + X: sprite.Origin.X * scale.X, + Y: sprite.Origin.Y * scale.Y, + }, + Dest: rl.Rectangle{X: position.X, Y: position.Y, Width: sprite.Frame.Width, Height: sprite.Frame.Height}, // + Tint: stdcomponents.Tint{ + R: 255, + G: 255, + B: 255, + A: 255, + }, + }) + } else { + tr.Dest.Width = sprite.Frame.Width + tr.Dest.Height = sprite.Frame.Height + } + return true + }) } -func (s *TextureRenderSpriteSystem) Destroy() {} +func (s *SpriteSystem) Destroy() {} From 0d58cb87c2bade11a47a4aa3df26863674f3d10d Mon Sep 17 00:00:00 2001 From: Kuzovlev Roman Date: Mon, 10 Mar 2025 18:24:21 +0700 Subject: [PATCH 041/196] change batch size + move wg.Add --- stdsystems/collision-detection.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 75f8b1f3..639dcb60 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -129,12 +129,12 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { maxNumWorkers := runtime.NumCPU() * 4 entitiesLength := len(entities) // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot entities - numWorkers := max(min(entitiesLength/10, maxNumWorkers), 1) + numWorkers := max(min(entitiesLength/32, maxNumWorkers), 1) chunkSize := entitiesLength / numWorkers - for i := 0; i < numWorkers; i++ { - wg.Add(1) + wg.Add(numWorkers) + for i := 0; i < numWorkers; i++ { startIndex := i * chunkSize endIndex := startIndex + chunkSize - 1 if i == numWorkers-1 { // have to set endIndex to entites lenght, if last worker From 60fe87d652f7cdd4fd0b5d432592eb002cbe213a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 11 Mar 2025 13:35:05 +0300 Subject: [PATCH 042/196] include donations 10.03.2025 --- examples/new-api/scenes/assterodd-scene.go | 3 + pkg/ecs/component-manager.go | 8 + pkg/ecs/entity.go | 3 +- stdsystems/collider.go | 2 +- stdsystems/collision-detection.go | 34 +++-- stdsystems/collision.go | 165 --------------------- 6 files changed, 37 insertions(+), 178 deletions(-) delete mode 100644 stdsystems/collision.go diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index 06667ec1..74db0cc1 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -8,6 +8,9 @@ Donations during this file development: -===-===-===-===-===-===-===-===-===-=== <- HromRu Donated 2 500 RUB +<- kotyamatroskin Donated 400 RUB +<- r_uslashk_a Donated 100 RUB +<- mitwelve Donated 100 RUB Thank you for your support! */ diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 9515e3a6..9475e285 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -2,6 +2,14 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +<- Hininn Donated 2 000 RUB + +Thank you for your support! */ package ecs diff --git a/pkg/ecs/entity.go b/pkg/ecs/entity.go index 14aaa602..6d6d83bb 100644 --- a/pkg/ecs/entity.go +++ b/pkg/ecs/entity.go @@ -7,7 +7,8 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- Basserti Donated 250 RUB +<- ubiblasosnalim Donated 100 RUB Thank you for your support! */ diff --git a/stdsystems/collider.go b/stdsystems/collider.go index c71db588..65124d49 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -7,7 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :) +<- rpecb Donated 500 RUB Thank you for your support! */ diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection.go index 74999d57..7aa440a6 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection.go @@ -7,7 +7,8 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Donations during this file development: -===-===-===-===-===-===-===-===-===-=== -none :)dw +<- SoundOfTheWind Donated 100 RUB +<- Anonymous Donated 500 RUB Thank you for your support! */ @@ -43,19 +44,30 @@ type CollisionDetectionSystem struct { cellSizeY int // Cache - activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities - currentCollisions map[CollisionPair]struct{} + activeCollisions map[collisionPair]ecs.Entity // Maps collision pairs to proxy entities + currentCollisions map[collisionPair]struct{} spatialBuckets map[stdcomponents.SpatialIndex][]ecs.Entity entityToCell map[ecs.Entity]stdcomponents.SpatialIndex - aabbs map[ecs.Entity]AABB + aabbs map[ecs.Entity]aabb } -type AABB struct { +type aabb struct { Left, Right, Top, Bottom float32 } +type collisionPair struct { + E1, E2 ecs.Entity +} + +func (c collisionPair) Normalize() collisionPair { + if c.E1 > c.E2 { + return collisionPair{c.E2, c.E1} + } + return c +} + func (s *CollisionDetectionSystem) Init() { - s.activeCollisions = make(map[CollisionPair]ecs.Entity) + s.activeCollisions = make(map[collisionPair]ecs.Entity) } type CollisionEvent struct { @@ -71,7 +83,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { for k := range s.spatialBuckets { delete(s.spatialBuckets, k) } - s.currentCollisions = make(map[CollisionPair]struct{}) + s.currentCollisions = make(map[collisionPair]struct{}) // Build spatial buckets and entity-to-cell map s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { @@ -88,12 +100,12 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { }) // Precompute AABBs for box colliders - s.aabbs = make(map[ecs.Entity]AABB, s.BoxColliders.Len()) + s.aabbs = make(map[ecs.Entity]aabb, s.BoxColliders.Len()) s.BoxColliders.EachEntity(func(entity ecs.Entity) bool { position := s.Positions.Get(entity) collider := s.BoxColliders.Get(entity) scale := s.Scales.Get(entity) - newAABB := AABB{ + newAABB := aabb{ Left: position.X - (collider.OffsetX * scale.X), Right: position.X + (collider.Width-collider.OffsetX)*scale.X, Top: position.Y - (collider.OffsetY * scale.Y), @@ -110,7 +122,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { // Start result collector go func() { for event := range collisionChan { - pair := CollisionPair{event.entityA, event.entityB}.Normalize() + pair := collisionPair{event.entityA, event.entityB}.Normalize() s.currentCollisions[pair] = struct{}{} if _, exists := s.activeCollisions[pair]; !exists { @@ -189,7 +201,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { func (s *CollisionDetectionSystem) Destroy() {} func (s *CollisionDetectionSystem) registerCollision(entityA, entityB ecs.Entity, posX, posY float32) { - pair := CollisionPair{entityA, entityB}.Normalize() + pair := collisionPair{entityA, entityB}.Normalize() s.currentCollisions[pair] = struct{}{} diff --git a/stdsystems/collision.go b/stdsystems/collision.go deleted file mode 100644 index 37968e91..00000000 --- a/stdsystems/collision.go +++ /dev/null @@ -1,165 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package stdsystems - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" - "sync" - "time" -) - -func NewCollisionSystem() CollisionSystem { - return CollisionSystem{} -} - -type CollisionSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - GenericCollider *stdcomponents.GenericColliderComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - Collisions *stdcomponents.CollisionComponentManager - - activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities - currentCollisions map[CollisionPair]struct{} - currentCollisionsMx sync.RWMutex - activeCollisionsMx sync.Mutex -} - -type CollisionPair struct { - E1, E2 ecs.Entity -} - -func (c CollisionPair) Normalize() CollisionPair { - if c.E1 > c.E2 { - return CollisionPair{c.E2, c.E1} - } - return c -} - -func (s *CollisionSystem) Init() { - s.activeCollisions = make(map[CollisionPair]ecs.Entity) -} -func (s *CollisionSystem) Run(dt time.Duration) { - s.currentCollisions = make(map[CollisionPair]struct{}) - - entities := s.GenericCollider.RawEntities(make([]ecs.Entity, 0, s.GenericCollider.Len())) - - const batchSize = 64 // Tune this based on performance testing - wg := new(sync.WaitGroup) - - // Process entities in batches - for start := 0; start < len(entities); start += batchSize { - end := start + batchSize - if end > len(entities) { - end = len(entities) - } - - wg.Add(1) - go func(start, end int) { - defer wg.Done() - for i := start; i < end; i++ { - entity := entities[i] - collision := s.GenericCollider.Get(entity) - - switch collision.Shape { - case stdcomponents.BoxColliderShape: - s.boxToXCollision(entities, i) - case stdcomponents.CircleColliderShape: - s.circleToXCollision(entities, i) - default: - panic("Unknown collision shape") - } - } - }(start, end) - } - - wg.Wait() - - s.processExitStates() -} -func (s *CollisionSystem) Destroy() {} - -func (s *CollisionSystem) registerCollision(entityA, entityB ecs.Entity) { - pair := CollisionPair{entityA, entityB}.Normalize() - - s.currentCollisionsMx.Lock() - s.currentCollisions[pair] = struct{}{} - s.currentCollisionsMx.Unlock() - - s.activeCollisionsMx.Lock() - defer s.activeCollisionsMx.Unlock() - - // Create proxy entity for new collisions - if _, exists := s.activeCollisions[pair]; !exists { - proxy := s.EntityManager.Create() - s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) - s.activeCollisions[pair] = proxy - } else { - s.Collisions.Get(s.activeCollisions[pair]).State = stdcomponents.CollisionStateStay - } -} - -func (s *CollisionSystem) processExitStates() { - s.activeCollisionsMx.Lock() - defer s.activeCollisionsMx.Unlock() - s.currentCollisionsMx.RLock() - defer s.currentCollisionsMx.RUnlock() - - for pair, proxy := range s.activeCollisions { - if _, exists := s.currentCollisions[pair]; !exists { - collision := s.Collisions.Get(proxy) - if collision.State == stdcomponents.CollisionStateExit { - delete(s.activeCollisions, pair) - s.EntityManager.Delete(proxy) - } else { - collision.State = stdcomponents.CollisionStateExit - } - } - } -} - -func (s *CollisionSystem) boxToXCollision(entities []ecs.Entity, i int) { - entityA := entities[i] - position1 := s.Positions.Get(entityA) - collider1 := s.BoxColliders.Get(entityA) - - for j := i + 1; j < len(entities); j++ { - entityB := entities[j] - position2 := s.Positions.Get(entityB) - genericCollider := s.GenericCollider.Get(entityB) - boxCollider := s.BoxColliders.Get(entityB) - - switch genericCollider.Shape { - case stdcomponents.BoxColliderShape: - // Check AABB collision - if !(position1.X+collider1.Width < position2.X || - position1.X > position2.X+boxCollider.Width || - position1.Y+collider1.Height < position2.Y || - position1.Y > position2.Y+boxCollider.Height) { - - s.registerCollision(entityA, entityB) - } - case stdcomponents.CircleColliderShape: - panic("Circle-Box collision not implemented") - default: - panic("Unknown collision shape") - } - } -} - -func (s *CollisionSystem) circleToXCollision(entities []ecs.Entity, i int) { - panic("Circle-X collision not implemented") -} From d0098e8169db730b6a6030f9d2f6046e88ce8927 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 13 Mar 2025 15:00:37 +0300 Subject: [PATCH 043/196] yolo --- examples/new-api/entities/spaceship.go | 2 +- examples/new-api/systems/spaceship-intents.go | 35 +++++++++++-------- pkg/ecs/paged-array.go | 11 ++++-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go index cea363cf..96293718 100644 --- a/examples/new-api/entities/spaceship.go +++ b/examples/new-api/entities/spaceship.go @@ -90,7 +90,7 @@ func CreateSpaceShip( props.Weapons.Create(spaceShip, components.Weapon{ Damage: 1, - Cooldown: time.Millisecond * 200, + Cooldown: time.Millisecond * 20, CooldownLeft: 0, }) diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index ceb553da..34311559 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -96,22 +96,29 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { vel.Y = float32(math.Cos(rads)) * s.moveSpeed vel.X = -float32(math.Sin(rads)) * s.moveSpeed - bulletVelocityY := vel.Y + float32(math.Cos(rads))*bulletSpeed - bulletVelocityX := vel.X - float32(math.Sin(rads))*bulletSpeed - if weapon.CooldownLeft <= 0 { if intent.Fire { - entities.CreateBullet(entities.CreateBulletManagers{ - EntityManager: s.EntityManager, - Positions: s.Positions, - Rotations: s.Rotations, - Scales: s.Scales, - Velocities: s.Velocities, - BoxColliders: s.BoxColliders, - Sprites: s.Sprites, - BulletTags: s.BulletTags, - Hps: s.Hps, - }, pos.X, pos.Y, rot.Angle, bulletVelocityX, bulletVelocityY) + count := 120 + ofs := 1 + startAngle := rot.Angle - float32(count*ofs/2) + for i := range count { + angle := startAngle + float32(i*ofs) + rads = deg2rad(float64(angle)) + math.Pi + + bulletVelocityY := vel.Y + float32(math.Cos(rads))*bulletSpeed + bulletVelocityX := vel.X - float32(math.Sin(rads))*bulletSpeed + entities.CreateBullet(entities.CreateBulletManagers{ + EntityManager: s.EntityManager, + Positions: s.Positions, + Rotations: s.Rotations, + Scales: s.Scales, + Velocities: s.Velocities, + BoxColliders: s.BoxColliders, + Sprites: s.Sprites, + BulletTags: s.BulletTags, + Hps: s.Hps, + }, pos.X, pos.Y, angle, bulletVelocityX, bulletVelocityY) + } weapon.CooldownLeft = weapon.Cooldown } } else { diff --git a/pkg/ecs/paged-array.go b/pkg/ecs/paged-array.go index 4545549a..fee6c93a 100644 --- a/pkg/ecs/paged-array.go +++ b/pkg/ecs/paged-array.go @@ -71,12 +71,17 @@ func (a *PagedArray[T]) Append(value T) *T { page := &a.book[a.currentPageIndex] - page.data[page.len] = value - result := &page.data[page.len] - page.len++ if page.len == pageSize { a.currentPageIndex++ + if a.currentPageIndex >= len(a.book) { + newBooks := make([]ArrayPage[T], len(a.book)*2) + a.book = append(a.book, newBooks...) + } + page = &a.book[a.currentPageIndex] } + page.data[page.len] = value + result := &page.data[page.len] + page.len++ a.len++ return result } From f0ac2b8a6eb239b7e4efa1dffdf1dad80f95fe89 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 14 Mar 2025 19:27:50 +0300 Subject: [PATCH 044/196] feat bvh system collision-detection-bvh.go --- asset-library.go | 4 +- examples/new-api/instances/component-list.go | 4 +- examples/new-api/instances/system-list.go | 8 +- examples/new-api/scenes/assterodd-scene.go | 6 +- examples/new-api/scenes/main-scene.go | 6 +- pkg/ecs/component-manager.go | 4 +- stdcomponents/aabb.go | 31 ++ stdcomponents/colliders.go | 16 +- stdcomponents/ids.go | 1 + stdsystems/collider.go | 44 +- stdsystems/collision-detection-bvh.go | 434 ++++++++++++++++++ ...tection.go => collision-detection-grid.go} | 38 +- vectors/vector2.go | 57 +++ 13 files changed, 610 insertions(+), 43 deletions(-) create mode 100644 stdcomponents/aabb.go create mode 100644 stdsystems/collision-detection-bvh.go rename stdsystems/{collision-detection.go => collision-detection-grid.go} (87%) create mode 100644 vectors/vector2.go diff --git a/asset-library.go b/asset-library.go index e162e749..8c972b9f 100644 --- a/asset-library.go +++ b/asset-library.go @@ -18,8 +18,8 @@ type AnyAssetLibrary interface { UnloadAll() } -func CreateAssetLibrary[T any](loader func(path string) T, unloader func(path string, asset *T)) *AssetLibrary[T] { - return &AssetLibrary[T]{ +func CreateAssetLibrary[T any](loader func(path string) T, unloader func(path string, asset *T)) AssetLibrary[T] { + return AssetLibrary[T]{ data: make(map[string]*T), loader: loader, unloader: unloader, diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index ac3c6ce7..093a0b8c 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -39,6 +39,7 @@ type ComponentList struct { ColliderBox stdcomponents.BoxColliderComponentManager ColliderCircle stdcomponents.CircleColliderComponentManager Collision stdcomponents.CollisionComponentManager + AABB stdcomponents.AABBComponentManager SpatialIndex stdcomponents.SpatialIndexComponentManager Health components.HpComponentManager @@ -72,8 +73,9 @@ func NewComponentList() ComponentList { RenderOrder: stdcomponents.NewRenderOrderComponentManager(), GenericCollider: stdcomponents.NewGenericColliderComponentManager(), ColliderBox: stdcomponents.NewBoxColliderComponentManager(), - ColliderCircle: stdcomponents.NewColliderCircleComponentManager(), + ColliderCircle: stdcomponents.NewCircleColliderComponentManager(), Collision: stdcomponents.NewCollisionComponentManager(), + AABB: stdcomponents.NewAABBComponentManager(), SpatialIndex: stdcomponents.NewSpatialIndexComponentManager(), Health: components.NewHealthComponentManager(), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index a7ae7d36..b377bfb6 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -34,9 +34,10 @@ func NewSystemList() SystemList { TextureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(), Sprite: stdsystems.NewSpriteSystem(), SpriteMatrix: stdsystems.NewSpriteMatrixSystem(), - AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{assets.Textures}), + AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{&assets.Textures}), YSort: stdsystems.NewYSortSystem(), - CollisionDetection: stdsystems.NewCollisionDetectionSystem(), + CollisionDetectionGrid: stdsystems.NewCollisionDetectionGridSystem(), + CollisionDetectionBVH: stdsystems.NewCollisionDetectionBVHSystem(), ColliderSystem: stdsystems.NewColliderSystem(), RenderAssterodd: systems.NewRenderAssteroddSystem(), RenderBogdan: systems.NewRenderBogdanSystem(), @@ -65,7 +66,8 @@ type SystemList struct { SpriteMatrix stdsystems.SpriteMatrixSystem AssetLib stdsystems.AssetLibSystem YSort stdsystems.YSortSystem - CollisionDetection stdsystems.CollisionDetectionSystem + CollisionDetectionGrid stdsystems.CollisionDetectionGridSystem + CollisionDetectionBVH stdsystems.CollisionDetectionBVHSystem ColliderSystem stdsystems.ColliderSystem RenderAssterodd systems.RenderAssteroddSystem RenderBogdan systems.RenderBogdanSystem diff --git a/examples/new-api/scenes/assterodd-scene.go b/examples/new-api/scenes/assterodd-scene.go index 74db0cc1..d40c27d8 100644 --- a/examples/new-api/scenes/assterodd-scene.go +++ b/examples/new-api/scenes/assterodd-scene.go @@ -81,7 +81,7 @@ func (s *AssteroddScene) Init() { s.World.Systems.SpaceshipIntents.Init() s.World.Systems.Velocity.Init() - s.World.Systems.CollisionDetection.Init() + s.World.Systems.CollisionDetectionBVH.Init() // Animation s.World.Systems.AnimationSpriteMatrix.Init() @@ -108,7 +108,7 @@ func (s *AssteroddScene) FixedUpdate(dt time.Duration) { s.World.Systems.SpaceshipIntents.Run(dt) s.World.Systems.Velocity.Run(dt) s.World.Systems.SpaceSpawner.Run(dt) - s.World.Systems.CollisionDetection.Run(dt) + s.World.Systems.CollisionDetectionBVH.Run(dt) s.World.Systems.CollisionHandler.Run(dt) s.World.Systems.Hp.Run(dt) } @@ -140,7 +140,7 @@ func (s *AssteroddScene) Destroy() { s.World.Systems.AssteroddSystem.Destroy() - s.World.Systems.CollisionDetection.Destroy() + s.World.Systems.CollisionDetectionBVH.Destroy() // Animation s.World.Systems.AnimationSpriteMatrix.Destroy() diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index dfd9c59d..bc5ae89c 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -47,7 +47,7 @@ func (s *MainScene) Init() { s.World.Systems.Player.Init() s.World.Systems.Velocity.Init() - s.World.Systems.CollisionDetection.Init() + s.World.Systems.CollisionDetectionGrid.Init() // Network patches s.World.Systems.NetworkSend.Init() @@ -78,7 +78,7 @@ func (s *MainScene) FixedUpdate(dt time.Duration) { s.World.Systems.Network.Run(dt) s.World.Systems.Velocity.Run(dt) - s.World.Systems.CollisionDetection.Run(dt) + s.World.Systems.CollisionDetectionGrid.Run(dt) s.World.Systems.CollisionHandler.Run(dt) s.World.Systems.NetworkSend.Run(dt) } @@ -108,7 +108,7 @@ func (s *MainScene) Destroy() { s.World.Systems.Player.Destroy() - s.World.Systems.CollisionDetection.Destroy() + s.World.Systems.CollisionDetectionGrid.Destroy() // Network patches s.World.Systems.NetworkSend.Destroy() diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index 9475e285..3357087b 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -358,8 +358,8 @@ func (c *ComponentManager[T]) IsTrackingChanges() bool { // Utils // ======================================================== -func (c *ComponentManager[T]) RawComponents(ptr []T) { - c.components.Raw(ptr) +func (c *ComponentManager[T]) RawComponents(ptr []T) []T { + return c.components.Raw(ptr) } func (c *ComponentManager[T]) RawEntities(ptr []Entity) []Entity { diff --git a/stdcomponents/aabb.go b/stdcomponents/aabb.go new file mode 100644 index 00000000..7bb7861b --- /dev/null +++ b/stdcomponents/aabb.go @@ -0,0 +1,31 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" + "gomp/vectors" +) + +type AABB struct { + Min vectors.Vec2 + Max vectors.Vec2 +} + +type AABBComponentManager = ecs.ComponentManager[AABB] + +func NewAABBComponentManager() AABBComponentManager { + return ecs.NewComponentManager[AABB](AABBComponentId) +} diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index d4461c8e..a09f7c91 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -47,16 +47,18 @@ func NewBoxColliderComponentManager() BoxColliderComponentManager { return ecs.NewComponentManager[BoxCollider](ColliderBoxComponentId) } -type ColliderCircle struct { - Radius float32 - Layer CollisionLayer - Mask CollisionMask +type CircleCollider struct { + Radius float32 + Layer CollisionLayer + Mask CollisionMask + OffsetX float32 + OffsetY float32 } -type CircleColliderComponentManager = ecs.ComponentManager[ColliderCircle] +type CircleColliderComponentManager = ecs.ComponentManager[CircleCollider] -func NewColliderCircleComponentManager() CircleColliderComponentManager { - return ecs.NewComponentManager[ColliderCircle](ColliderCircleComponentId) +func NewCircleColliderComponentManager() CircleColliderComponentManager { + return ecs.NewComponentManager[CircleCollider](ColliderCircleComponentId) } type GenericCollider struct { diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 57d6eef2..4ecf30a2 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -33,5 +33,6 @@ const ( ColliderCircleComponentId CollisionComponentId SpatialIndexComponentId + AABBComponentId StdComponentIds ) diff --git a/stdsystems/collider.go b/stdsystems/collider.go index 65124d49..58b338af 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -17,6 +17,7 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" + "gomp/vectors" "time" ) @@ -26,9 +27,12 @@ func NewColliderSystem() ColliderSystem { type ColliderSystem struct { EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Scales *stdcomponents.ScaleComponentManager GenericColliders *stdcomponents.GenericColliderComponentManager BoxColliders *stdcomponents.BoxColliderComponentManager CircleColliders *stdcomponents.CircleColliderComponentManager + AABB *stdcomponents.AABBComponentManager } func (s *ColliderSystem) Init() {} @@ -47,6 +51,22 @@ func (s *ColliderSystem) Run(dt time.Duration) { }) } + position := s.Positions.Get(entity) + scale := s.Scales.Get(entity) + aabb := s.AABB.Get(entity) + if aabb == nil { + aabb = s.AABB.Create(entity, stdcomponents.AABB{}) + } + + aabb.Min = vectors.Vec2{ + X: position.X - (boxCollider.OffsetX * scale.X), + Y: position.Y - (boxCollider.OffsetY * scale.Y), + } + aabb.Max = vectors.Vec2{ + X: position.X + (boxCollider.Width-boxCollider.OffsetX)*scale.X, + Y: position.Y + (boxCollider.Height-boxCollider.OffsetY)*scale.Y, + } + return true }) @@ -56,12 +76,30 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider := s.GenericColliders.Get(entity) if genCollider == nil { s.GenericColliders.Create(entity, stdcomponents.GenericCollider{ - Shape: stdcomponents.CircleColliderShape, - Layer: circleCollider.Layer, - Mask: circleCollider.Mask, + Shape: stdcomponents.CircleColliderShape, + Layer: circleCollider.Layer, + Mask: circleCollider.Mask, + OffsetX: circleCollider.OffsetX, + OffsetY: circleCollider.OffsetY, }) } + position := s.Positions.Get(entity) + scale := s.Scales.Get(entity) + aabb := s.AABB.Get(entity) + if aabb == nil { + aabb = s.AABB.Create(entity, stdcomponents.AABB{}) + } + + aabb.Min = vectors.Vec2{ + X: position.X - (circleCollider.OffsetX * scale.X), + Y: position.Y + (circleCollider.Radius-circleCollider.OffsetY)*scale.Y, + } + aabb.Max = vectors.Vec2{ + X: position.X + (circleCollider.Radius-circleCollider.OffsetX)*scale.X, + Y: position.Y - (circleCollider.OffsetY * scale.Y), + } + return true }) } diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go new file mode 100644 index 00000000..a73e14e0 --- /dev/null +++ b/stdsystems/collision-detection-bvh.go @@ -0,0 +1,434 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "math" + "math/bits" + "runtime" + "slices" + "sync" + "time" +) + +// BVHNode represents a node in the BVH tree +type BVHNode struct { + Left, Right int // Child indices, -1 for leaf + Entity ecs.Entity + Bounds stdcomponents.AABB +} + +func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { + return CollisionDetectionBVHSystem{ + activeCollisions: make(map[CollisionPair]ecs.Entity), + } +} + +type CollisionDetectionBVHSystem struct { + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Scales *stdcomponents.ScaleComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + Collisions *stdcomponents.CollisionComponentManager + SpatialIndex *stdcomponents.SpatialIndexComponentManager + AABB *stdcomponents.AABBComponentManager + + nodes []BVHNode + + activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities + currentCollisions map[CollisionPair]struct{} +} + +type treeObject struct { + Entity ecs.Entity + Bound stdcomponents.AABB + MortonCode uint32 +} + +func (s *CollisionDetectionBVHSystem) Init() {} +func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { + if s.AABB.Len() == 0 { + return + } + + s.currentCollisions = make(map[CollisionPair]struct{}) + + // Sorting by morton code + treeObjects := make([]treeObject, 0, s.AABB.Len()) + s.AABB.EachEntity(func(entity ecs.Entity) bool { + aabbBound := s.AABB.Get(entity) + center := aabbBound.Min.Add(aabbBound.Max).Scale(0.5) + newTreeObject := treeObject{ + Entity: entity, + Bound: *aabbBound, + MortonCode: morton2D(center.X, center.Y), + } + treeObjects = append(treeObjects, newTreeObject) + return true + }) + slices.SortFunc(treeObjects, func(a, b treeObject) int { + return int(a.MortonCode) - int(b.MortonCode) + }) + + // extract data to the arrays + entities := make([]ecs.Entity, len(treeObjects)) + aabbs := make([]stdcomponents.AABB, len(treeObjects)) + mortonCodes := make([]uint32, len(treeObjects)) + for i, obj := range treeObjects { + entities[i] = obj.Entity + aabbs[i] = obj.Bound + mortonCodes[i] = obj.MortonCode + } + + s.buildBVH(entities, aabbs, mortonCodes) + + // Create collision channel + collisionChan := make(chan CollisionEvent, 4096*runtime.NumCPU()) + doneChan := make(chan struct{}) + + // Start result collector + go func() { + for event := range collisionChan { + pair := CollisionPair{event.entityA, event.entityB}.Normalize() + s.currentCollisions[pair] = struct{}{} + + if _, exists := s.activeCollisions[pair]; !exists { + proxy := s.EntityManager.Create() + s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) + s.Positions.Create(proxy, stdcomponents.Position{X: event.posX, Y: event.posY}) + s.activeCollisions[pair] = proxy + } else { + proxy := s.activeCollisions[pair] + s.Collisions.Get(proxy).State = stdcomponents.CollisionStateStay + s.Positions.Get(proxy).X = event.posX + s.Positions.Get(proxy).Y = event.posY + } + } + close(doneChan) + }() + + s.findEntityCollisions(entities, aabbs, collisionChan) + + close(collisionChan) + <-doneChan // Wait for result collector + s.processExitStates() +} +func (s *CollisionDetectionBVHSystem) Destroy() {} + +func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, collisionChan chan<- CollisionEvent) { + var wg sync.WaitGroup + maxNumWorkers := runtime.NumCPU() + entitiesLength := len(entities) + // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities + numWorkers := max(min(entitiesLength/32, maxNumWorkers), 1) + chunkSize := entitiesLength / numWorkers + + wg.Add(numWorkers) + for workedId := 0; workedId < numWorkers; workedId++ { + startIndex := workedId * chunkSize + endIndex := startIndex + chunkSize - 1 + if workedId == numWorkers-1 { // have to set endIndex to entities length, if last worker + endIndex = entitiesLength + } + rootIndex := len(s.nodes) - 1 + + go func(start int, end int) { + defer wg.Done() + + for i := range entities[start:end] { + s.traverseBVHForCollisions(entities, aabbs, i+startIndex, rootIndex, collisionChan) + } + }(startIndex, endIndex) + } + // Wait for workers and close collision channel + wg.Wait() + +} + +func (s *CollisionDetectionBVHSystem) traverseBVHForCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, i int, nodeIndex int, collisionChan chan<- CollisionEvent) { + node := s.nodes[nodeIndex] + entityA := entities[i] + aabbA := aabbs[i] + + if node.Left == -1 && node.Right == -1 { + j := nodeIndex // Leaf nodes are ordered as per entities slice + if j > i { + entityB := entities[j] + if aabbOverlap(aabbA, node.Bounds) { + colliderA := s.GenericCollider.Get(entityA) + colliderB := s.GenericCollider.Get(entityB) + + if colliderA.Mask&(1< split the range in the middle. + first := sortedMortonCodes[start] + last := sortedMortonCodes[end] + + if first == last { + return (start + end) >> 1 + } + + // Calculate the number of highest bits that are the same + // for all objects, using the count-leading-zeros intrinsic. + commonPrefix := bits.LeadingZeros32(first ^ last) + + // Use binary search to find where the next bit differs. + // Specifically, we are looking for the highest object that + // shares more than commonPrefix bits with the first one. + split := start + step := end - start + + for { + step = (step + 1) >> 1 // exponential decrease + newSplit := split + step // proposed new position + + if newSplit < end { + splitCode := sortedMortonCodes[newSplit] + splitPrefix := bits.LeadingZeros32(first ^ splitCode) + if splitPrefix > commonPrefix { + split = newSplit + } + } + + if step <= 1 { + break + } + } + + return split +} + +// aabbOverlap checks if two AABBs intersect +func aabbOverlap(a, b stdcomponents.AABB) bool { + return a.Max.X >= b.Min.X && a.Min.X <= b.Max.X && + a.Max.Y >= b.Min.Y && a.Min.Y <= b.Max.Y +} + +// mergeAABB combines two AABBs +func mergeAABB(a, b stdcomponents.AABB) stdcomponents.AABB { + return stdcomponents.AABB{ + Min: vectors.Vec2{ + X: min(a.Min.X, b.Min.X), + Y: min(a.Min.Y, b.Min.Y), + }, + Max: vectors.Vec2{ + X: max(a.Max.X, b.Max.X), + Y: max(a.Max.Y, b.Max.Y), + }, + } +} + +// Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit +func expandBits2D(v uint32) uint32 { + v = (v * 0x00010001) & 0xFF0000FF + v = (v * 0x00000101) & 0x0F00F00F + v = (v * 0x00000011) & 0xC30C30C3 + v = (v * 0x00000005) & 0x24924924 + return v +} + +// 2D Morton code for coordinates in [0,1] range +func morton2D(x, y float32) uint32 { + xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) + yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) + return (expandBits2D(xx) << 1) | expandBits2D(yy) +} + +// Expands a 10-bit integer into 30 bits by inserting 2 zeros after each bit +func expandBits3D(v uint32) uint32 { + v = (v * 0x00010001) & 0xFF0000FF + v = (v * 0x00000101) & 0x0F00F00F + v = (v * 0x00000011) & 0xC30C30C3 + v = (v * 0x00000005) & 0x49249249 + return v +} + +// 3D Morton code for coordinates in [0,1] range +func morton3D(x, y, z float32) uint32 { + xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) + yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) + zz := uint32(math.Min(math.Max(float64(z)*1024.0, 0.0), 1023.0)) + return expandBits3D(xx)*4 + expandBits3D(yy)*2 + expandBits3D(zz) +} diff --git a/stdsystems/collision-detection.go b/stdsystems/collision-detection-grid.go similarity index 87% rename from stdsystems/collision-detection.go rename to stdsystems/collision-detection-grid.go index 7aa440a6..a7acee3c 100644 --- a/stdsystems/collision-detection.go +++ b/stdsystems/collision-detection-grid.go @@ -23,15 +23,15 @@ import ( "time" ) -func NewCollisionDetectionSystem() CollisionDetectionSystem { - return CollisionDetectionSystem{ +func NewCollisionDetectionGridSystem() CollisionDetectionGridSystem { + return CollisionDetectionGridSystem{ cellSizeX: 192, cellSizeY: 192, spatialBuckets: make(map[stdcomponents.SpatialIndex][]ecs.Entity, 32), } } -type CollisionDetectionSystem struct { +type CollisionDetectionGridSystem struct { EntityManager *ecs.EntityManager Positions *stdcomponents.PositionComponentManager Scales *stdcomponents.ScaleComponentManager @@ -44,8 +44,8 @@ type CollisionDetectionSystem struct { cellSizeY int // Cache - activeCollisions map[collisionPair]ecs.Entity // Maps collision pairs to proxy entities - currentCollisions map[collisionPair]struct{} + activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities + currentCollisions map[CollisionPair]struct{} spatialBuckets map[stdcomponents.SpatialIndex][]ecs.Entity entityToCell map[ecs.Entity]stdcomponents.SpatialIndex aabbs map[ecs.Entity]aabb @@ -55,19 +55,19 @@ type aabb struct { Left, Right, Top, Bottom float32 } -type collisionPair struct { +type CollisionPair struct { E1, E2 ecs.Entity } -func (c collisionPair) Normalize() collisionPair { +func (c CollisionPair) Normalize() CollisionPair { if c.E1 > c.E2 { - return collisionPair{c.E2, c.E1} + return CollisionPair{c.E2, c.E1} } return c } -func (s *CollisionDetectionSystem) Init() { - s.activeCollisions = make(map[collisionPair]ecs.Entity) +func (s *CollisionDetectionGridSystem) Init() { + s.activeCollisions = make(map[CollisionPair]ecs.Entity) } type CollisionEvent struct { @@ -75,7 +75,7 @@ type CollisionEvent struct { posX, posY float32 } -func (s *CollisionDetectionSystem) Run(dt time.Duration) { +func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { if len(s.entityToCell) < s.GenericCollider.Len() { s.entityToCell = make(map[ecs.Entity]stdcomponents.SpatialIndex, s.GenericCollider.Len()) } @@ -83,7 +83,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { for k := range s.spatialBuckets { delete(s.spatialBuckets, k) } - s.currentCollisions = make(map[collisionPair]struct{}) + s.currentCollisions = make(map[CollisionPair]struct{}) // Build spatial buckets and entity-to-cell map s.GenericCollider.EachEntity(func(entity ecs.Entity) bool { @@ -122,7 +122,7 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { // Start result collector go func() { for event := range collisionChan { - pair := collisionPair{event.entityA, event.entityB}.Normalize() + pair := CollisionPair{event.entityA, event.entityB}.Normalize() s.currentCollisions[pair] = struct{}{} if _, exists := s.activeCollisions[pair]; !exists { @@ -198,10 +198,10 @@ func (s *CollisionDetectionSystem) Run(dt time.Duration) { s.processExitStates() } -func (s *CollisionDetectionSystem) Destroy() {} +func (s *CollisionDetectionGridSystem) Destroy() {} -func (s *CollisionDetectionSystem) registerCollision(entityA, entityB ecs.Entity, posX, posY float32) { - pair := collisionPair{entityA, entityB}.Normalize() +func (s *CollisionDetectionGridSystem) registerCollision(entityA, entityB ecs.Entity, posX, posY float32) { + pair := CollisionPair{entityA, entityB}.Normalize() s.currentCollisions[pair] = struct{}{} @@ -218,7 +218,7 @@ func (s *CollisionDetectionSystem) registerCollision(entityA, entityB ecs.Entity } } -func (s *CollisionDetectionSystem) processExitStates() { +func (s *CollisionDetectionGridSystem) processExitStates() { for pair, proxy := range s.activeCollisions { if _, exists := s.currentCollisions[pair]; !exists { collision := s.Collisions.Get(proxy) @@ -232,7 +232,7 @@ func (s *CollisionDetectionSystem) processExitStates() { } } -func (s *CollisionDetectionSystem) boxToXCollision(entityA ecs.Entity, collisionChan chan<- CollisionEvent) { +func (s *CollisionDetectionGridSystem) boxToXCollision(entityA ecs.Entity, collisionChan chan<- CollisionEvent) { position1 := s.Positions.Get(entityA) spatialIndex1 := s.entityToCell[entityA] genericCollider1 := s.GenericCollider.Get(entityA) @@ -293,6 +293,6 @@ func (s *CollisionDetectionSystem) boxToXCollision(entityA ecs.Entity, collision } } -func (s *CollisionDetectionSystem) circleToXCollision(entityA ecs.Entity) { +func (s *CollisionDetectionGridSystem) circleToXCollision(entityA ecs.Entity) { panic("Circle-X collision not implemented") } diff --git a/vectors/vector2.go b/vectors/vector2.go new file mode 100644 index 00000000..92960967 --- /dev/null +++ b/vectors/vector2.go @@ -0,0 +1,57 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package vectors + +import "math" + +type Vec2 struct { + X, Y float32 +} + +func (v Vec2) Add(other Vec2) Vec2 { + return Vec2{v.X + other.X, v.Y + other.Y} +} + +func (v Vec2) Sub(other Vec2) Vec2 { + return Vec2{v.X - other.X, v.Y - other.Y} +} + +func (v Vec2) Mul(other Vec2) Vec2 { + return Vec2{v.X * other.X, v.Y * other.Y} +} + +func (v Vec2) Div(other Vec2) Vec2 { + return Vec2{v.X / other.X, v.Y / other.Y} +} + +func (v Vec2) AddScalar(scalar float32) Vec2 { + return Vec2{v.X + scalar, v.Y + scalar} +} + +func (v Vec2) SubScalar(scalar float32) Vec2 { + return Vec2{v.X - scalar, v.Y - scalar} +} + +func (v Vec2) Scale(scalar float32) Vec2 { + return Vec2{v.X * scalar, v.Y * scalar} +} + +func (v Vec2) Length() float32 { + return float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y))) +} + +func (v Vec2) Normalize() Vec2 { + return v.Scale(1 / v.Length()) +} From fc7d3d3ef21346b016305bd03fc130e58069008e Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 14 Mar 2025 20:07:17 +0300 Subject: [PATCH 045/196] refactor collision-detection-bvh.go to use stack instead of function recursive call in traverseBVHForCollisions() --- stdsystems/collision-detection-bvh.go | 99 +++++++++++++++------------ 1 file changed, 57 insertions(+), 42 deletions(-) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index a73e14e0..a73ae861 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -162,51 +162,60 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity } -func (s *CollisionDetectionBVHSystem) traverseBVHForCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, i int, nodeIndex int, collisionChan chan<- CollisionEvent) { - node := s.nodes[nodeIndex] +func (s *CollisionDetectionBVHSystem) traverseBVHForCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, i int, rootIndex int, collisionChan chan<- CollisionEvent) { entityA := entities[i] aabbA := aabbs[i] - if node.Left == -1 && node.Right == -1 { - j := nodeIndex // Leaf nodes are ordered as per entities slice - if j > i { - entityB := entities[j] - if aabbOverlap(aabbA, node.Bounds) { - colliderA := s.GenericCollider.Get(entityA) - colliderB := s.GenericCollider.Get(entityB) - - if colliderA.Mask&(1< 0 { + // Pop the last node from the stack + nodeIndex := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + node := s.nodes[nodeIndex] + + if node.Left == -1 && node.Right == -1 { + // Leaf node: Check collision with the current entity (i) if j > i + j := nodeIndex + if j > i { + entityB := entities[j] + // Check AABB overlap between entityA and entityB + if s.aabbOverlap(aabbA, node.Bounds) { + colliderA := s.GenericCollider.Get(entityA) + colliderB := s.GenericCollider.Get(entityB) + + // Check layer masks + if colliderA.Mask&(1<= b.Min.X && a.Min.X <= b.Max.X && - a.Max.Y >= b.Min.Y && a.Min.Y <= b.Max.Y +func (s *CollisionDetectionBVHSystem) aabbOverlap(a, b stdcomponents.AABB) bool { + // Check for non-overlap conditions first (early exit) + if a.Max.X < b.Min.X || a.Min.X > b.Max.X { + return false + } + if a.Max.Y < b.Min.Y || a.Min.Y > b.Max.Y { + return false + } + return true } // mergeAABB combines two AABBs From d746766f4efa08814b5d4ee4661b4ff5358fd495 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 15 Mar 2025 01:47:27 +0300 Subject: [PATCH 046/196] small optimizations --- examples/new-api/systems/render-assterodd.go | 17 ++++++----- examples/new-api/systems/spaceship-intents.go | 12 ++++---- stdsystems/collider.go | 14 ++++----- stdsystems/collision-detection-bvh.go | 30 +++++++++++-------- taskfile.yml | 4 +-- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/examples/new-api/systems/render-assterodd.go b/examples/new-api/systems/render-assterodd.go index 935b7565..4ab831fd 100644 --- a/examples/new-api/systems/render-assterodd.go +++ b/examples/new-api/systems/render-assterodd.go @@ -27,7 +27,9 @@ import ( ) func NewRenderAssteroddSystem() RenderAssteroddSystem { - return RenderAssteroddSystem{} + return RenderAssteroddSystem{ + instanceData: make([]stdcomponents.RLTexturePro, 0, 8192), + } } type RenderAssteroddSystem struct { @@ -142,19 +144,18 @@ func (s *RenderAssteroddSystem) render() { // Batch and render var currentTex = -1 - var instanceData []stdcomponents.RLTexturePro = make([]stdcomponents.RLTexturePro, 0, 8192) for i := range s.renderList { entry := &s.renderList[i] - if entry.TextureId != currentTex || len(instanceData) >= 8192 { - if len(instanceData) > 0 { - s.submitBatch(currentTex, instanceData) - instanceData = instanceData[:0] + if entry.TextureId != currentTex || len(s.instanceData) >= 8192 { + if len(s.instanceData) > 0 { + s.submitBatch(currentTex, s.instanceData) + s.instanceData = s.instanceData[:0] } currentTex = entry.TextureId } - instanceData = append(instanceData, s.getInstanceData(entry.Entity)) + s.instanceData = append(s.instanceData, s.getInstanceData(entry.Entity)) } - s.submitBatch(currentTex, instanceData) // Submit last batch + s.submitBatch(currentTex, s.instanceData) // Submit last batch s.renderList = s.renderList[:0] rl.BeginMode2D(s.camera) diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index 34311559..ca376a8c 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -58,11 +58,11 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { pos := s.Positions.Get(entity) weapon := s.Weapons.Get(entity) - if pos.Y < 0 || pos.Y > 5000 || pos.X < 0 || pos.X > 5000 { - vel.X *= -1 - vel.Y *= -1 - rot.Angle += 180 - } + //if pos.Y < 0 || pos.Y > 5000 || pos.X < 0 || pos.X > 5000 { + // vel.X *= -1 + // vel.Y *= -1 + // rot.Angle += 180 + //} if intent.RotateLeft { rot.Angle -= rotateSpeed @@ -98,7 +98,7 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { if weapon.CooldownLeft <= 0 { if intent.Fire { - count := 120 + count := 360 ofs := 1 startAngle := rot.Angle - float32(count*ofs/2) for i := range count { diff --git a/stdsystems/collider.go b/stdsystems/collider.go index 58b338af..cac060a5 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -42,14 +42,13 @@ func (s *ColliderSystem) Run(dt time.Duration) { genCollider := s.GenericColliders.Get(entity) if genCollider == nil { - s.GenericColliders.Create(entity, stdcomponents.GenericCollider{ - Shape: stdcomponents.BoxColliderShape, - Layer: boxCollider.Layer, - Mask: boxCollider.Mask, - OffsetX: boxCollider.OffsetX, - OffsetY: boxCollider.OffsetY, - }) + genCollider = s.GenericColliders.Create(entity, stdcomponents.GenericCollider{}) } + genCollider.Layer = boxCollider.Layer + genCollider.Mask = boxCollider.Mask + genCollider.OffsetX = boxCollider.OffsetX + genCollider.OffsetY = boxCollider.OffsetY + genCollider.Shape = stdcomponents.BoxColliderShape position := s.Positions.Get(entity) scale := s.Scales.Get(entity) @@ -57,7 +56,6 @@ func (s *ColliderSystem) Run(dt time.Duration) { if aabb == nil { aabb = s.AABB.Create(entity, stdcomponents.AABB{}) } - aabb.Min = vectors.Vec2{ X: position.X - (boxCollider.OffsetX * scale.X), Y: position.Y - (boxCollider.OffsetY * scale.Y), diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index a73ae861..775c165c 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -100,7 +100,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { s.buildBVH(entities, aabbs, mortonCodes) // Create collision channel - collisionChan := make(chan CollisionEvent, 4096*runtime.NumCPU()) + collisionChan := make(chan CollisionEvent, 4096) doneChan := make(chan struct{}) // Start result collector @@ -134,10 +134,10 @@ func (s *CollisionDetectionBVHSystem) Destroy() {} func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, collisionChan chan<- CollisionEvent) { var wg sync.WaitGroup - maxNumWorkers := runtime.NumCPU() + maxNumWorkers := runtime.NumCPU() - 2 entitiesLength := len(entities) // get minimum 1 worker for small amount of entities, and maximum maxNumWorkers for a lot of entities - numWorkers := max(min(entitiesLength/32, maxNumWorkers), 1) + numWorkers := max(min(entitiesLength/128, maxNumWorkers), 1) chunkSize := entitiesLength / numWorkers wg.Add(numWorkers) @@ -164,17 +164,19 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity func (s *CollisionDetectionBVHSystem) traverseBVHForCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, i int, rootIndex int, collisionChan chan<- CollisionEvent) { entityA := entities[i] - aabbA := aabbs[i] + aabbA := &aabbs[i] stack := make([]int, 0, 64) stack = append(stack, rootIndex) + lenstack := len(stack) - for len(stack) > 0 { + for lenstack > 0 { // Pop the last node from the stack nodeIndex := stack[len(stack)-1] stack = stack[:len(stack)-1] + lenstack-- - node := s.nodes[nodeIndex] + node := &s.nodes[nodeIndex] if node.Left == -1 && node.Right == -1 { // Leaf node: Check collision with the current entity (i) if j > i @@ -182,7 +184,7 @@ func (s *CollisionDetectionBVHSystem) traverseBVHForCollisions(entities []ecs.En if j > i { entityB := entities[j] // Check AABB overlap between entityA and entityB - if s.aabbOverlap(aabbA, node.Bounds) { + if s.aabbOverlap(aabbA, &node.Bounds) { colliderA := s.GenericCollider.Get(entityA) colliderB := s.GenericCollider.Get(entityB) @@ -206,15 +208,17 @@ func (s *CollisionDetectionBVHSystem) traverseBVHForCollisions(entities []ecs.En } } else { // Internal node: Check children and push to stack if overlapping - leftNode := s.nodes[node.Left] - rightNode := s.nodes[node.Right] + leftNode := &s.nodes[node.Left] + rightNode := &s.nodes[node.Right] // Push right first to process left first (stack is LIFO) - if s.aabbOverlap(aabbA, rightNode.Bounds) { + if s.aabbOverlap(aabbA, &rightNode.Bounds) { stack = append(stack, node.Right) + lenstack++ } - if s.aabbOverlap(aabbA, leftNode.Bounds) { + if s.aabbOverlap(aabbA, &leftNode.Bounds) { stack = append(stack, node.Left) + lenstack++ } } } @@ -389,8 +393,8 @@ func findSplit(sortedMortonCodes []uint32, start, end int) int { return split } -// aabbOverlap checks if two AABBs intersect -func (s *CollisionDetectionBVHSystem) aabbOverlap(a, b stdcomponents.AABB) bool { +// go:inline aabbOverlap checks if two AABBs intersect +func (s *CollisionDetectionBVHSystem) aabbOverlap(a, b *stdcomponents.AABB) bool { // Check for non-overlap conditions first (early exit) if a.Max.X < b.Min.X || a.Min.X > b.Max.X { return false diff --git a/taskfile.yml b/taskfile.yml index 6153d1b6..be9358f4 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -47,8 +47,8 @@ tasks: CGO_ENABLED: 1 cmds: - go build -o ./.dist/game-win64.exe examples/new-api/game.go - - go build -o ./.dist/cgo-game.exe internal/sdl3-pure/sdl-game.go - - CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -tags static -ldflags "-s -w" internal/sdl2/sdl-game.go +# - go build -o ./.dist/cgo-game.exe internal/sdl3-pure/sdl-game.go +# - CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -tags static -ldflags "-s -w" internal/sdl2/sdl-game.go build-mac: - task: build-darwin-amd64 From b833dab241d4a715e44f10588a2dab27c2c1745f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 15 Mar 2025 15:24:28 +0300 Subject: [PATCH 047/196] refactor collision-detection-bvh.go - implement layers for trees --- examples/new-api/game.go | 2 +- pkg/bvh/tree.go | 226 ++++++++++++++++++++++++++ stdcomponents/colliders.go | 4 + stdsystems/collision-detection-bvh.go | 123 ++++++++++---- 4 files changed, 325 insertions(+), 30 deletions(-) create mode 100644 pkg/bvh/tree.go diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 995c6c84..76748ec5 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -29,5 +29,5 @@ func main() { game.CurrentSceneId = scenes.AssteroddSceneId engine := gomp.NewEngine(&game) - engine.Run(20, 0) + engine.Run(50, 0) } diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go new file mode 100644 index 00000000..9e41feb4 --- /dev/null +++ b/pkg/bvh/tree.go @@ -0,0 +1,226 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package bvh + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "gomp/vectors" + "math" + "math/bits" + "slices" +) + +// Node represents a node in the BVH tree +type Node struct { + Left, Right int // Child indices, -1 for leaf + MortonCode uint32 + Entity ecs.Entity + Bounds stdcomponents.AABB +} + +func (n *Node) isLeaf() bool { + return n.Left == -1 && n.Right == -1 +} + +func NewTree2D(layer stdcomponents.CollisionLayer, prealloc int) Tree2D { + return Tree2D{ + layer: layer, + components: make([]treeComponent, 0, prealloc), + } +} + +// Tree2D represents a BVH tree +type Tree2D struct { + layer stdcomponents.CollisionLayer + nodes []Node + components []treeComponent +} + +type treeComponent struct { + Entity ecs.Entity + AABB stdcomponents.AABB +} + +func (t *Tree2D) Layer() stdcomponents.CollisionLayer { + return t.layer +} + +func (t *Tree2D) Query(aabb stdcomponents.AABB, handler func(entity ecs.Entity)) { + // Use stack-based traversal (as previously optimized) + stack := make([]int, 0, 64) + stack = append(stack, len(t.nodes)-1) + for len(stack) > 0 { + nodeIndex := stack[len(stack)-1] + stack = stack[:len(stack)-1] + node := t.nodes[nodeIndex] + + // Early exit if no AABB overlap + if !t.aabbOverlap(&aabb, &node.Bounds) { + continue + } + + if node.isLeaf() { + // Check detailed collision with node.Entity + // (Same as your existing collision logic) + //result = append(result, node.Entity) + handler(node.Entity) + } else { + // Push children to stack + stack = append(stack, node.Right, node.Left) + } + } +} + +func (t *Tree2D) AddComponent(entity ecs.Entity, aabbs stdcomponents.AABB) { + t.components = append(t.components, treeComponent{ + Entity: entity, + AABB: aabbs, + }) +} + +func (t *Tree2D) Build() { + t.nodes = make([]Node, 0, len(t.components)*2) + + // Create leaf nodes + leaves := make([]Node, len(t.components)) + for i := range t.components { + aabb := t.components[i].AABB + center := aabb.Min.Add(aabb.Max).Scale(0.5) + code := t.morton2D(center.X, center.Y) + leaves[i] = Node{ + Left: -1, + Right: -1, + MortonCode: code, + Entity: t.components[i].Entity, + Bounds: aabb, + } + } + t.components = t.components[:0] + + // Sort leaf nodes by morton code + slices.SortFunc(leaves, func(a, b Node) int { + return int(a.MortonCode) - int(b.MortonCode) + }) + + t.nodes = append(t.nodes, leaves...) + + t.buildHierarchy(0, len(leaves)-1) +} + +func (t *Tree2D) buildHierarchy(start, end int) int { + if start == end { + return start // Leaf node + } + + // Find split point using the highest differing bit + split := t.findSplit(start, end) + + // Recursively build left and right subtrees + left := t.buildHierarchy(start, split) + right := t.buildHierarchy(split+1, end) + + // Create internal node + node := Node{ + Left: left, + Right: right, + Bounds: t.mergeAABB(&t.nodes[left].Bounds, &t.nodes[right].Bounds), + } + + t.nodes = append(t.nodes, node) + return len(t.nodes) - 1 +} + +// findSplit finds the position where the highest bit changes +func (t *Tree2D) findSplit(start, end int) int { + // Identical Morton sortedMortonCodes => split the range in the middle. + first := t.nodes[start].MortonCode + last := t.nodes[end].MortonCode + + if first == last { + return (start + end) >> 1 + } + + // Calculate the number of highest bits that are the same + // for all objects, using the count-leading-zeros intrinsic. + commonPrefix := bits.LeadingZeros32(first ^ last) + + // Use binary search to find where the next bit differs. + // Specifically, we are looking for the highest object that + // shares more than commonPrefix bits with the first one. + split := start + step := end - start + + for { + step = (step + 1) >> 1 // exponential decrease + newSplit := split + step // proposed new position + + if newSplit < end { + splitCode := t.nodes[newSplit].MortonCode + splitPrefix := bits.LeadingZeros32(first ^ splitCode) + if splitPrefix > commonPrefix { + split = newSplit + } + } + + if step <= 1 { + break + } + } + + return split +} + +// mergeAABB combines two AABB +func (t *Tree2D) mergeAABB(a, b *stdcomponents.AABB) stdcomponents.AABB { + return stdcomponents.AABB{ + Min: vectors.Vec2{ + X: min(a.Min.X, b.Min.X), + Y: min(a.Min.Y, b.Min.Y), + }, + Max: vectors.Vec2{ + X: max(a.Max.X, b.Max.X), + Y: max(a.Max.Y, b.Max.Y), + }, + } +} + +// go:inline aabbOverlap checks if two AABB intersect +func (t *Tree2D) aabbOverlap(a, b *stdcomponents.AABB) bool { + // Check for non-overlap conditions first (early exit) + if a.Max.X < b.Min.X || a.Min.X > b.Max.X { + return false + } + if a.Max.Y < b.Min.Y || a.Min.Y > b.Max.Y { + return false + } + return true +} + +// Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit +func (t *Tree2D) expandBits2D(v uint32) uint32 { + v = (v * 0x00010001) & 0xFF0000FF + v = (v * 0x00000101) & 0x0F00F00F + v = (v * 0x00000011) & 0xC30C30C3 + v = (v * 0x00000005) & 0x24924924 + return v +} + +// 2D Morton code for centroids coordinates in [0,1] range +func (t *Tree2D) morton2D(x, y float32) uint32 { + xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) + yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) + return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) +} diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index a09f7c91..127d37b2 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -26,6 +26,10 @@ const ( type CollisionMask uint64 +func (m CollisionMask) HasLayer(layer CollisionLayer) bool { + return m&(1<= entityB { + return + } + + colliderB := s.GenericCollider.Get(entityB) + + if s.checkCollision(*colliderA, *colliderB, entityA, entityB) { + posA := s.Positions.Get(entityA) + posB := s.Positions.Get(entityB) + posX := (posA.X + posB.X) / 2 + posY := (posA.Y + posB.Y) / 2 + collisionChan <- CollisionEvent{ + entityA: entityA, + entityB: entityB, + posX: posX, + posY: posY, + } + } + }) + + //for _, entityB := range entities { + // if entityA >= entityB { + // continue + // } + // + // colliderB := s.GenericCollider.Get(entityB) + // + // if s.checkCollision(*colliderA, *colliderB, entityA, entityB) { + // posA := s.Positions.Get(entityA) + // posB := s.Positions.Get(entityB) + // posX := (posA.X + posB.X) / 2 + // posY := (posA.Y + posB.Y) / 2 + // collisionChan <- CollisionEvent{ + // entityA: entityA, + // entityB: entityB, + // posX: posX, + // posY: posY, + // } + // } + // + //} + } } func (s *CollisionDetectionBVHSystem) traverseBVHForCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, i int, rootIndex int, collisionChan chan<- CollisionEvent) { @@ -393,7 +458,7 @@ func findSplit(sortedMortonCodes []uint32, start, end int) int { return split } -// go:inline aabbOverlap checks if two AABBs intersect +// go:inline aabbOverlap checks if two AABB intersect func (s *CollisionDetectionBVHSystem) aabbOverlap(a, b *stdcomponents.AABB) bool { // Check for non-overlap conditions first (early exit) if a.Max.X < b.Min.X || a.Min.X > b.Max.X { @@ -405,7 +470,7 @@ func (s *CollisionDetectionBVHSystem) aabbOverlap(a, b *stdcomponents.AABB) bool return true } -// mergeAABB combines two AABBs +// mergeAABB combines two AABB func mergeAABB(a, b stdcomponents.AABB) stdcomponents.AABB { return stdcomponents.AABB{ Min: vectors.Vec2{ From c233834bbbe311efa1197d4079051413421d239f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 15 Mar 2025 16:22:43 +0300 Subject: [PATCH 048/196] optimize bvh tree --- pkg/bvh/tree.go | 93 ++++++++--- stdsystems/collision-detection-bvh.go | 219 +------------------------- 2 files changed, 71 insertions(+), 241 deletions(-) diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 9e41feb4..1cca28a6 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -47,6 +47,7 @@ type Tree2D struct { layer stdcomponents.CollisionLayer nodes []Node components []treeComponent + rootIndex int } type treeComponent struct { @@ -59,9 +60,14 @@ func (t *Tree2D) Layer() stdcomponents.CollisionLayer { } func (t *Tree2D) Query(aabb stdcomponents.AABB, handler func(entity ecs.Entity)) { - // Use stack-based traversal (as previously optimized) + if t.rootIndex == -1 || len(t.nodes) == 0 { // Handle empty tree + return + } + + // Use stack-based traversal stack := make([]int, 0, 64) - stack = append(stack, len(t.nodes)-1) + stack = append(stack, t.rootIndex) + for len(stack) > 0 { nodeIndex := stack[len(stack)-1] stack = stack[:len(stack)-1] @@ -75,7 +81,6 @@ func (t *Tree2D) Query(aabb stdcomponents.AABB, handler func(entity ecs.Entity)) if node.isLeaf() { // Check detailed collision with node.Entity // (Same as your existing collision logic) - //result = append(result, node.Entity) handler(node.Entity) } else { // Push children to stack @@ -91,8 +96,24 @@ func (t *Tree2D) AddComponent(entity ecs.Entity, aabbs stdcomponents.AABB) { }) } +type TaskType int + +const ( + BuildTaskType TaskType = iota + MergeTaskType +) + +type Task struct { + Type TaskType + Start int + End int +} + func (t *Tree2D) Build() { - t.nodes = make([]Node, 0, len(t.components)*2) + if cap(t.nodes) < len(t.components)*2 { + t.nodes = make([]Node, 0, len(t.components)*2) + } + t.nodes = t.nodes[:0] // Create leaf nodes leaves := make([]Node, len(t.components)) @@ -117,30 +138,52 @@ func (t *Tree2D) Build() { t.nodes = append(t.nodes, leaves...) - t.buildHierarchy(0, len(leaves)-1) -} - -func (t *Tree2D) buildHierarchy(start, end int) int { - if start == end { - return start // Leaf node + // Stack-based hierarchy construction + var resultStack []int + taskStack := []Task{{Type: BuildTaskType, Start: 0, End: len(leaves) - 1}} + + for len(taskStack) > 0 { + task := taskStack[len(taskStack)-1] + taskStack = taskStack[:len(taskStack)-1] + + switch task.Type { + case BuildTaskType: + start, end := task.Start, task.End + if start == end { + // Leaf node: push its index to result stack + resultStack = append(resultStack, start) + } else { + split := t.findSplit(start, end) + // Schedule MergeTask after processing children + taskStack = append(taskStack, Task{Type: MergeTaskType}) + // Process right child first (LIFO order) + taskStack = append(taskStack, Task{Type: BuildTaskType, Start: split + 1, End: end}) + // Process left child next + taskStack = append(taskStack, Task{Type: BuildTaskType, Start: start, End: split}) + } + case MergeTaskType: + // Pop right then left from result stack + right := resultStack[len(resultStack)-1] + resultStack = resultStack[:len(resultStack)-1] + left := resultStack[len(resultStack)-1] + resultStack = resultStack[:len(resultStack)-1] + + // Create parent node and append to t.nodes + parent := Node{ + Left: left, + Right: right, + Bounds: t.mergeAABB(&t.nodes[left].Bounds, &t.nodes[right].Bounds), + } + t.nodes = append(t.nodes, parent) + // Push parent index to result stack + resultStack = append(resultStack, len(t.nodes)-1) + } } - // Find split point using the highest differing bit - split := t.findSplit(start, end) - - // Recursively build left and right subtrees - left := t.buildHierarchy(start, split) - right := t.buildHierarchy(split+1, end) - - // Create internal node - node := Node{ - Left: left, - Right: right, - Bounds: t.mergeAABB(&t.nodes[left].Bounds, &t.nodes[right].Bounds), + // After processing all tasks, resultStack holds root index + if len(resultStack) > 0 { + t.rootIndex = resultStack[0] } - - t.nodes = append(t.nodes, node) - return len(t.nodes) - 1 } // findSplit finds the position where the highest bit changes diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index b3dbc724..c79b58cc 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -20,7 +20,6 @@ import ( "gomp/stdcomponents" "gomp/vectors" "math" - "math/bits" "runtime" "sync" "time" @@ -53,21 +52,14 @@ type CollisionDetectionBVHSystem struct { trees []bvh.Tree2D treesLookup map[stdcomponents.CollisionLayer]int - nodes []BVHNode - activeCollisions map[CollisionPair]ecs.Entity // Maps collision pairs to proxy entities currentCollisions map[CollisionPair]struct{} } -type treeObject struct { - Entity ecs.Entity - Bound stdcomponents.AABB - MortonCode uint32 -} - func (s *CollisionDetectionBVHSystem) Init() {} func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { s.currentCollisions = make(map[CollisionPair]struct{}) + defer s.processExitStates() if s.AABB.Len() == 0 { return @@ -103,7 +95,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { wg.Wait() // Create collision channel - collisionChan := make(chan CollisionEvent, 4096) + collisionChan := make(chan CollisionEvent, 4096*4) doneChan := make(chan struct{}) // Start result collector @@ -134,7 +126,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { close(collisionChan) <-doneChan // Wait for result collector - s.processExitStates() + } func (s *CollisionDetectionBVHSystem) Destroy() {} @@ -202,90 +194,6 @@ func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, } } }) - - //for _, entityB := range entities { - // if entityA >= entityB { - // continue - // } - // - // colliderB := s.GenericCollider.Get(entityB) - // - // if s.checkCollision(*colliderA, *colliderB, entityA, entityB) { - // posA := s.Positions.Get(entityA) - // posB := s.Positions.Get(entityB) - // posX := (posA.X + posB.X) / 2 - // posY := (posA.Y + posB.Y) / 2 - // collisionChan <- CollisionEvent{ - // entityA: entityA, - // entityB: entityB, - // posX: posX, - // posY: posY, - // } - // } - // - //} - } -} - -func (s *CollisionDetectionBVHSystem) traverseBVHForCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, i int, rootIndex int, collisionChan chan<- CollisionEvent) { - entityA := entities[i] - aabbA := &aabbs[i] - - stack := make([]int, 0, 64) - stack = append(stack, rootIndex) - lenstack := len(stack) - - for lenstack > 0 { - // Pop the last node from the stack - nodeIndex := stack[len(stack)-1] - stack = stack[:len(stack)-1] - lenstack-- - - node := &s.nodes[nodeIndex] - - if node.Left == -1 && node.Right == -1 { - // Leaf node: Check collision with the current entity (i) if j > i - j := nodeIndex - if j > i { - entityB := entities[j] - // Check AABB overlap between entityA and entityB - if s.aabbOverlap(aabbA, &node.Bounds) { - colliderA := s.GenericCollider.Get(entityA) - colliderB := s.GenericCollider.Get(entityB) - - // Check layer masks - if colliderA.Mask&(1< split the range in the middle. - first := sortedMortonCodes[start] - last := sortedMortonCodes[end] - - if first == last { - return (start + end) >> 1 - } - - // Calculate the number of highest bits that are the same - // for all objects, using the count-leading-zeros intrinsic. - commonPrefix := bits.LeadingZeros32(first ^ last) - - // Use binary search to find where the next bit differs. - // Specifically, we are looking for the highest object that - // shares more than commonPrefix bits with the first one. - split := start - step := end - start - - for { - step = (step + 1) >> 1 // exponential decrease - newSplit := split + step // proposed new position - - if newSplit < end { - splitCode := sortedMortonCodes[newSplit] - splitPrefix := bits.LeadingZeros32(first ^ splitCode) - if splitPrefix > commonPrefix { - split = newSplit - } - } - - if step <= 1 { - break - } - } - - return split -} - -// go:inline aabbOverlap checks if two AABB intersect -func (s *CollisionDetectionBVHSystem) aabbOverlap(a, b *stdcomponents.AABB) bool { - // Check for non-overlap conditions first (early exit) - if a.Max.X < b.Min.X || a.Min.X > b.Max.X { - return false - } - if a.Max.Y < b.Min.Y || a.Min.Y > b.Max.Y { - return false - } - return true -} - -// mergeAABB combines two AABB -func mergeAABB(a, b stdcomponents.AABB) stdcomponents.AABB { - return stdcomponents.AABB{ - Min: vectors.Vec2{ - X: min(a.Min.X, b.Min.X), - Y: min(a.Min.Y, b.Min.Y), - }, - Max: vectors.Vec2{ - X: max(a.Max.X, b.Max.X), - Y: max(a.Max.Y, b.Max.Y), - }, - } -} - -// Expands a 10-bit integer into 20 bits by inserting 1 zero after each bit -func expandBits2D(v uint32) uint32 { - v = (v * 0x00010001) & 0xFF0000FF - v = (v * 0x00000101) & 0x0F00F00F - v = (v * 0x00000011) & 0xC30C30C3 - v = (v * 0x00000005) & 0x24924924 - return v -} - -// 2D Morton code for coordinates in [0,1] range -func morton2D(x, y float32) uint32 { - xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) - yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) - return (expandBits2D(xx) << 1) | expandBits2D(yy) } // Expands a 10-bit integer into 30 bits by inserting 2 zeros after each bit From 9bb6f6024780030cd833619cd720976d0a2edc8a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 15 Mar 2025 16:36:56 +0300 Subject: [PATCH 049/196] fix window render --- render.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/render.go b/render.go index 2d4b400e..32f9b4f8 100644 --- a/render.go +++ b/render.go @@ -26,7 +26,10 @@ func NewRenderSystem() RenderSystem { type RenderSystem struct{} func (s *RenderSystem) Init() { - rl.InitWindow(0, 0, "raylib [core] ebiten-ecs - basic window") + monitor := rl.GetCurrentMonitor() + width, height := rl.GetMonitorWidth(monitor), rl.GetMonitorHeight(monitor) + rl.InitWindow(int32(width), int32(height), "raylib [core] ebiten-ecs - basic window") + rl.SetWindowState(rl.FlagFullscreenMode) } func (s *RenderSystem) Run(dt time.Duration) bool { if rl.WindowShouldClose() { From 4a27e59ab7ab99553d900f61d94f89a6b96d41d6 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 15 Mar 2025 19:20:11 +0300 Subject: [PATCH 050/196] better collision mask and layer system --- examples/new-api/config/config.go | 1 + examples/new-api/entities/asteroid.go | 2 +- examples/new-api/entities/bullet.go | 4 ++-- examples/new-api/entities/wall.go | 2 +- examples/new-api/game.go | 2 +- stdsystems/collision-detection-bvh.go | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/new-api/config/config.go b/examples/new-api/config/config.go index 6a904ab6..32bd3a2f 100644 --- a/examples/new-api/config/config.go +++ b/examples/new-api/config/config.go @@ -19,6 +19,7 @@ import "gomp/stdcomponents" const ( DefaultCollisionLayer stdcomponents.CollisionLayer = iota PlayerCollisionLayer + BulletCollisionLayer EnemyCollisionLayer WallCollisionLayer ) diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index ed80fef6..a4047706 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -66,7 +66,7 @@ func CreateAsteroid( OffsetX: 16, OffsetY: 16, Layer: config.EnemyCollisionLayer, - Mask: 1 << config.PlayerCollisionLayer, + Mask: 0, }) props.Sprites.Create(bullet, stdcomponents.Sprite{ Texture: assets.Textures.Get("meteor_large.png"), diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index edf06a41..0be476c6 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -63,8 +63,8 @@ func CreateBullet( Height: 16, OffsetX: 8, OffsetY: 8, - Layer: config.PlayerCollisionLayer, - Mask: 1 << config.EnemyCollisionLayer, + Layer: config.BulletCollisionLayer, + Mask: 1<= entityB { + if entityA == entityB { return } From 7be447dbcad2c1198dc397d2699a25eca5ce32d7 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 18 Mar 2025 19:36:54 +0300 Subject: [PATCH 051/196] add steamworks api --- examples/new-api/game.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 995c6c84..6ce0e880 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -15,11 +15,40 @@ Thank you for your support! package main import ( + "github.com/hajimehoshi/go-steamworks" + "golang.org/x/text/language" "gomp" "gomp/examples/new-api/scenes" + "log" + "os" ) +const appID = 12 // Rewrite this + +func init() { + if steamworks.RestartAppIfNecessary(appID) { + os.Exit(1) + } + err := steamworks.Init() + if err != nil { + panic("steamworks.Init failed") + } +} + +func SystemLang() language.Tag { + switch steamworks.SteamApps().GetCurrentGameLanguage() { + case "russian": + return language.Russian + case "english": + return language.English + case "japanese": + return language.Japanese + } + return language.Und +} + func main() { + log.Println(SystemLang()) sceneList := scenes.NewSceneList() game := gomp.NewGame( From 49d79dd48c4189baa4dd3a82ea61d48f89ab91b9 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 18 Mar 2025 19:37:46 +0300 Subject: [PATCH 052/196] collision-detection-bvh.go implement GJK --- go.mod | 1 + go.sum | 2 + pkg/bvh/tree.go | 17 ++ stdcomponents/colliders.go | 22 ++- stdcomponents/ids.go | 1 + stdsystems/collision-detection-bvh.go | 230 ++++++++++++++++++++++---- steam_appid.txt | 1 + taskfile.yml | 2 +- vectors/vector2.go | 20 +++ 9 files changed, 259 insertions(+), 37 deletions(-) create mode 100644 steam_appid.txt diff --git a/go.mod b/go.mod index 8033b0ca..53d2c6cc 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect + github.com/hajimehoshi/go-steamworks v0.0.0-20241112125913-96b2a6baef69 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect diff --git a/go.sum b/go.sum index a7f2676c..2a7140cf 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,8 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hajimehoshi/ebiten/v2 v2.8.6 h1:Dkd/sYI0TYyZRCE7GVxV59XC+WCi2BbGAbIBjXeVC1U= github.com/hajimehoshi/ebiten/v2 v2.8.6/go.mod h1:cCQ3np7rdmaJa1ZnvslraVlpxNb3wCjEnAP1LHNyXNA= +github.com/hajimehoshi/go-steamworks v0.0.0-20241112125913-96b2a6baef69 h1:R5DmT3Ffuccaf3U7DiLYpro2avyBz7D112v9eqm8NvE= +github.com/hajimehoshi/go-steamworks v0.0.0-20241112125913-96b2a6baef69/go.mod h1:xQbwn4VSK2CwjfAgpolFH8MYSu96NQZhiOuksu1vvdY= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jakecoffman/cp/v2 v2.1.0 h1:s0almZ7zDZs9JY35ciUgCoVKTMmdPkokF1dxHg226Wo= github.com/jakecoffman/cp/v2 v2.1.0/go.mod h1:Q0hFU7Kk6PMw4dwgFtvBC6O4KTm7ewiLuHrXtHMicyU= diff --git a/pkg/bvh/tree.go b/pkg/bvh/tree.go index 1cca28a6..a05f2048 100644 --- a/pkg/bvh/tree.go +++ b/pkg/bvh/tree.go @@ -267,3 +267,20 @@ func (t *Tree2D) morton2D(x, y float32) uint32 { yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) return (t.expandBits2D(xx) << 1) | t.expandBits2D(yy) } + +// Expands a 10-bit integer into 30 bits by inserting 2 zeros after each bit +func expandBits3D(v uint32) uint32 { + v = (v * 0x00010001) & 0xFF0000FF + v = (v * 0x00000101) & 0x0F00F00F + v = (v * 0x00000011) & 0xC30C30C3 + v = (v * 0x00000005) & 0x49249249 + return v +} + +// 3D Morton code for coordinates in [0,1] range +func morton3D(x, y, z float32) uint32 { + xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) + yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) + zz := uint32(math.Min(math.Max(float64(z)*1024.0, 0.0), 1023.0)) + return expandBits3D(xx)*4 + expandBits3D(yy)*2 + expandBits3D(zz) +} diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 127d37b2..b195ec25 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -14,7 +14,10 @@ Thank you for your support! package stdcomponents -import "gomp/pkg/ecs" +import ( + "gomp/pkg/ecs" + "gomp/vectors" +) type ColliderShape uint8 @@ -22,6 +25,7 @@ const ( InvalidColliderShape ColliderShape = iota BoxColliderShape CircleColliderShape + PolygonColliderShape ) type CollisionMask uint64 @@ -33,7 +37,7 @@ func (m CollisionMask) HasLayer(layer CollisionLayer) bool { type CollisionLayer = CollisionMask const ( - ColliderLayerNone CollisionLayer = 0 + CollisionLayerNone CollisionLayer = 0 ) type BoxCollider struct { @@ -65,6 +69,20 @@ func NewCircleColliderComponentManager() CircleColliderComponentManager { return ecs.NewComponentManager[CircleCollider](ColliderCircleComponentId) } +type PolygonCollider struct { + Vertices []vectors.Vec2 + Layer CollisionLayer + Mask CollisionMask + OffsetX float32 + OffsetY float32 +} + +type PolygonColliderComponentManager = ecs.ComponentManager[PolygonCollider] + +func NewPolygonColliderComponentManager() PolygonColliderComponentManager { + return ecs.NewComponentManager[PolygonCollider](PolygonColliderComponentId) +} + type GenericCollider struct { Shape ColliderShape Layer CollisionLayer diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index 4ecf30a2..345b5128 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -31,6 +31,7 @@ const ( GenericColliderComponentId ColliderBoxComponentId ColliderCircleComponentId + PolygonColliderComponentId CollisionComponentId SpatialIndexComponentId AABBComponentId diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 8fcf53ab..b3768be3 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -25,13 +25,6 @@ import ( "time" ) -// BVHNode represents a node in the BVH tree -type BVHNode struct { - Left, Right int // Child indices, -1 for leaf - Entity ecs.Entity - Bounds stdcomponents.AABB -} - func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { return CollisionDetectionBVHSystem{ activeCollisions: make(map[CollisionPair]ecs.Entity), @@ -39,15 +32,17 @@ func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { } type CollisionDetectionBVHSystem struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Scales *stdcomponents.ScaleComponentManager - GenericCollider *stdcomponents.GenericColliderComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - CircleColliders *stdcomponents.CircleColliderComponentManager - Collisions *stdcomponents.CollisionComponentManager - SpatialIndex *stdcomponents.SpatialIndexComponentManager - AABB *stdcomponents.AABBComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + GenericCollider *stdcomponents.GenericColliderComponentManager + BoxColliders *stdcomponents.BoxColliderComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + PolygonColliders *stdcomponents.PolygonColliderComponentManager + Collisions *stdcomponents.CollisionComponentManager + SpatialIndex *stdcomponents.SpatialIndexComponentManager + AABB *stdcomponents.AABBComponentManager trees []bvh.Tree2D treesLookup map[stdcomponents.CollisionLayer]int @@ -101,7 +96,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { // Start result collector go func() { for event := range collisionChan { - pair := CollisionPair{event.entityA, event.entityB}.Normalize() + pair := CollisionPair{event.entityA, event.entityB} s.currentCollisions[pair] = struct{}{} if _, exists := s.activeCollisions[pair]; !exists { @@ -181,7 +176,7 @@ func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, colliderB := s.GenericCollider.Get(entityB) - if s.checkCollision(*colliderA, *colliderB, entityA, entityB) { + if s.checkCollisionGjk(*colliderA, *colliderB, entityA, entityB) { posA := s.Positions.Get(entityA) posB := s.Positions.Get(entityB) posX := (posA.X + posB.X) / 2 @@ -197,6 +192,21 @@ func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, } } +func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB stdcomponents.GenericCollider, entityA, entityB ecs.Entity) bool { + posA := s.Positions.Get(entityA) + posB := s.Positions.Get(entityB) + scaleA := s.getScaleOrDefault(entityA) + scaleB := s.getScaleOrDefault(entityB) + rotA := s.getRotationOrDefault(entityA) // Implement similar to getScaleOrDefault + rotB := s.getRotationOrDefault(entityB) + + // Define support functions based on collider types + supportA := s.getSupportFunction(entityA, colliderA, posA, &rotA, scaleA) + supportB := s.getSupportFunction(entityB, colliderB, posB, &rotB, scaleB) + + return s.gjkCollides(supportA, supportB) +} + func (s *CollisionDetectionBVHSystem) checkCollision(colliderA, colliderB stdcomponents.GenericCollider, entityA, entityB ecs.Entity) bool { posA := s.Positions.Get(entityA) posB := s.Positions.Get(entityB) @@ -238,13 +248,23 @@ func (s *CollisionDetectionBVHSystem) checkCollision(colliderA, colliderB stdcom } func (s *CollisionDetectionBVHSystem) getScaleOrDefault(entity ecs.Entity) vectors.Vec2 { - if s.Scales.Has(entity) { - scale := s.Scales.Get(entity) - return vectors.Vec2{X: scale.X, Y: scale.Y} + scale := s.Scales.Get(entity) + if scale != nil { + return vectors.Vec2{X: scale.X, Y: scale.Y} // Dereference the component pointer } + // Return default scale of 1 if component doesn't exist return vectors.Vec2{X: 1, Y: 1} } +func (s *CollisionDetectionBVHSystem) getRotationOrDefault(entity ecs.Entity) stdcomponents.Rotation { + rotation := s.Rotations.Get(entity) + if rotation != nil { + return *rotation // Dereference the component pointer + } + // Return default zero rotation if component doesn't exist + return stdcomponents.Rotation{Angle: 0} +} + func (s *CollisionDetectionBVHSystem) circleVsBox(circleCollider *stdcomponents.CircleCollider, circlePos stdcomponents.Position, circleScale vectors.Vec2, boxCollider *stdcomponents.BoxCollider, boxPos stdcomponents.Position, boxScale vectors.Vec2) bool { radius := circleCollider.Radius * circleScale.X boxWidth := boxCollider.Width * boxScale.X @@ -287,19 +307,161 @@ func (s *CollisionDetectionBVHSystem) buildBVH(entities []ecs.Entity, aabbs []st } -// Expands a 10-bit integer into 30 bits by inserting 2 zeros after each bit -func expandBits3D(v uint32) uint32 { - v = (v * 0x00010001) & 0xFF0000FF - v = (v * 0x00000101) & 0x0F00F00F - v = (v * 0x00000011) & 0xC30C30C3 - v = (v * 0x00000005) & 0x49249249 - return v +func (s *CollisionDetectionBVHSystem) getSupportFunction(entity ecs.Entity, collider stdcomponents.GenericCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2) func(vectors.Vec2) vectors.Vec2 { + switch collider.Shape { + case stdcomponents.BoxColliderShape: + box := s.BoxColliders.Get(entity) + return func(d vectors.Vec2) vectors.Vec2 { + return s.boxSupport(box, pos, rot, scale, d) + } + case stdcomponents.CircleColliderShape: + circle := s.CircleColliders.Get(entity) + return func(d vectors.Vec2) vectors.Vec2 { + return s.circleSupport(circle, pos, scale, d) + } + case stdcomponents.PolygonColliderShape: + poly := s.PolygonColliders.Get(entity) + return func(d vectors.Vec2) vectors.Vec2 { + return s.polygonSupport(poly, pos, rot, scale, d) + } + default: + panic("unsupported collider shape") + } +} + +func (s *CollisionDetectionBVHSystem) circleSupport(circle *stdcomponents.CircleCollider, pos *stdcomponents.Position, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { + if direction.LengthSquared() == 0 { + return vectors.Vec2{X: pos.X, Y: pos.Y} + } + radius := circle.Radius * scale.X + dirNorm := direction.Normalize() + return vectors.Vec2{ + X: pos.X + dirNorm.X*radius, + Y: pos.Y + dirNorm.Y*radius, + } +} + +func (s *CollisionDetectionBVHSystem) boxSupport(box *stdcomponents.BoxCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { + hw := (box.Width * scale.X) / 2 + hh := (box.Height * scale.Y) / 2 + + // Rotate direction to local space + localDir := direction.Rotate(-rot.Angle) + + localX := hw + if localDir.X < 0 { + localX = -hw + } + localY := hh + if localDir.Y < 0 { + localY = -hh + } + + // Rotate back to world space and translate + worldPoint := vectors.Vec2{X: localX, Y: localY}.Rotate(rot.Angle) + return vectors.Vec2{ + X: pos.X + worldPoint.X, + Y: pos.Y + worldPoint.Y, + } +} + +func (s *CollisionDetectionBVHSystem) polygonSupport(poly *stdcomponents.PolygonCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { + maxDot := math.Inf(-1) + var maxVertex vectors.Vec2 + + for _, v := range poly.Vertices { + scaled := vectors.Vec2{X: v.X * scale.X, Y: v.Y * scale.Y} + rotated := scaled.Rotate(rot.Angle) + worldVertex := vectors.Vec2{X: pos.X + rotated.X, Y: pos.Y + rotated.Y} + dot := float64(worldVertex.Dot(direction)) + if dot > maxDot { + maxDot = dot + maxVertex = worldVertex + } + } + return maxVertex +} + +func (s *CollisionDetectionBVHSystem) gjkCollides(supportA, supportB func(vectors.Vec2) vectors.Vec2) bool { + direction := vectors.Vec2{X: 1, Y: 0} // Initial direction + simplex := []vectors.Vec2{} + for i := 0; i < 50; i++ { // Max iterations to prevent infinite loop + p := s.minkowskiSupport(supportA, supportB, direction) + if p.Dot(direction) < 0 { + return false // No collision + } + simplex = append(simplex, p) + if s.containsOrigin(simplex, direction) { + return true + } + } + return false +} + +func (s *CollisionDetectionBVHSystem) minkowskiSupport(supportA, supportB func(vectors.Vec2) vectors.Vec2, d vectors.Vec2) vectors.Vec2 { + a := supportA(d) + b := supportB(d.Neg()) + return a.Sub(b) +} + +func (s *CollisionDetectionBVHSystem) containsOrigin(simplex []vectors.Vec2, direction vectors.Vec2) bool { + a := (simplex)[len(simplex)-1] // Last point added + ao := a.Neg() // Vector from A to origin + + switch len(simplex) { + case 3: // Triangle case + b := (simplex)[1] + c := (simplex)[0] + + ab := b.Sub(a) + ac := c.Sub(a) + + // Perpendicular vectors + abPerp := s.tripleProduct(ac, ab, ab) + acPerp := s.tripleProduct(ab, ac, ac) + + // Region AB + if abPerp.Dot(ao) > 0 { + simplex = []vectors.Vec2{a, b} + direction = abPerp + return false + } + + // Region AC + if acPerp.Dot(ao) > 0 { + simplex = []vectors.Vec2{a, c} + direction = acPerp + return false + } + + // Inside triangle + return true + + case 2: // Line segment case + b := (simplex)[0] + ab := b.Sub(a) + + // Perpendicular to AB facing origin + abPerp := s.tripleProduct(ab, ao, ab) + if abPerp.Dot(ao) > 0 { + direction = abPerp + } else { + simplex = []vectors.Vec2{a} + direction = ao + } + return false + + default: + return false + } } -// 3D Morton code for coordinates in [0,1] range -func morton3D(x, y, z float32) uint32 { - xx := uint32(math.Min(math.Max(float64(x)*1024.0, 0.0), 1023.0)) - yy := uint32(math.Min(math.Max(float64(y)*1024.0, 0.0), 1023.0)) - zz := uint32(math.Min(math.Max(float64(z)*1024.0, 0.0), 1023.0)) - return expandBits3D(xx)*4 + expandBits3D(yy)*2 + expandBits3D(zz) +// Helper function for vector triple product +func (s *CollisionDetectionBVHSystem) tripleProduct(a, b, c vectors.Vec2) vectors.Vec2 { + ac := a.Dot(c) + bc := b.Dot(c) + return vectors.Vec2{ + X: b.X*ac - a.X*bc, + Y: b.Y*ac - a.Y*bc, + } } diff --git a/steam_appid.txt b/steam_appid.txt new file mode 100644 index 00000000..7ad80225 --- /dev/null +++ b/steam_appid.txt @@ -0,0 +1 @@ +480 \ No newline at end of file diff --git a/taskfile.yml b/taskfile.yml index be9358f4..3dc7ce2f 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -46,7 +46,7 @@ tasks: env: CGO_ENABLED: 1 cmds: - - go build -o ./.dist/game-win64.exe examples/new-api/game.go + - go build -o ./.dist/game-win64.exe -tags opengl43 examples/new-api/game.go # - go build -o ./.dist/cgo-game.exe internal/sdl3-pure/sdl-game.go # - CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -tags static -ldflags "-s -w" internal/sdl2/sdl-game.go diff --git a/vectors/vector2.go b/vectors/vector2.go index 92960967..e01a81ca 100644 --- a/vectors/vector2.go +++ b/vectors/vector2.go @@ -55,3 +55,23 @@ func (v Vec2) Length() float32 { func (v Vec2) Normalize() Vec2 { return v.Scale(1 / v.Length()) } + +func (v Vec2) Rotate(angle float32) Vec2 { + return Vec2{ + v.X*float32(math.Cos(float64(angle))) - v.Y*float32(math.Sin(float64(angle))), + v.X*float32(math.Sin(float64(angle))) + v.Y*float32(math.Cos(float64(angle))), + } +} + +func (v Vec2) LengthSquared() float32 { + l := v.Length() + return l * l +} + +func (v Vec2) Neg() Vec2 { + return Vec2{-v.X, -v.Y} +} + +func (v Vec2) Dot(other Vec2) float32 { + return v.X*other.X + v.Y*other.Y +} From 9c57aa4cb6138faea85e641b01179f90b0d97eb5 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 18 Mar 2025 20:47:48 +0300 Subject: [PATCH 053/196] collision-detection-bvh.go implement epa - collision depth and normal --- render.go | 8 +- stdcomponents/collision.go | 7 +- stdsystems/collision-detection-bvh.go | 145 +++++++++++++++++++++---- stdsystems/collision-detection-grid.go | 5 +- vectors/vector2.go | 13 +++ 5 files changed, 149 insertions(+), 29 deletions(-) diff --git a/render.go b/render.go index 32f9b4f8..d0fc1e9a 100644 --- a/render.go +++ b/render.go @@ -26,10 +26,10 @@ func NewRenderSystem() RenderSystem { type RenderSystem struct{} func (s *RenderSystem) Init() { - monitor := rl.GetCurrentMonitor() - width, height := rl.GetMonitorWidth(monitor), rl.GetMonitorHeight(monitor) - rl.InitWindow(int32(width), int32(height), "raylib [core] ebiten-ecs - basic window") - rl.SetWindowState(rl.FlagFullscreenMode) + //monitor := rl.GetCurrentMonitor() + //width, height := rl.GetMonitorWidth(monitor), rl.GetMonitorHeight(monitor) + rl.InitWindow(1280, 720, "raylib [core] ebiten-ecs - basic window") + //rl.SetWindowState(rl.FlagFullscreenMode) } func (s *RenderSystem) Run(dt time.Duration) bool { if rl.WindowShouldClose() { diff --git a/stdcomponents/collision.go b/stdcomponents/collision.go index 81a79ab7..438f756c 100644 --- a/stdcomponents/collision.go +++ b/stdcomponents/collision.go @@ -14,7 +14,10 @@ Thank you for your support! package stdcomponents -import "gomp/pkg/ecs" +import ( + "gomp/pkg/ecs" + "gomp/vectors" +) type CollisionState uint8 @@ -29,6 +32,8 @@ const ( type Collision struct { E1, E2 ecs.Entity State CollisionState + Normal vectors.Vec2 // Collision normal (direction) + Depth float32 // Penetration depth } type CollisionComponentManager = ecs.ComponentManager[Collision] diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index b3768be3..b5391377 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -25,6 +25,12 @@ import ( "time" ) +const ( + EPA_TOLERANCE = 0.0001 + EPA_MAX_ITERATIONS = 50 + MIN_NORMAL_LENGTH = 0.00001 +) + func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { return CollisionDetectionBVHSystem{ activeCollisions: make(map[CollisionPair]ecs.Entity), @@ -98,17 +104,30 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { for event := range collisionChan { pair := CollisionPair{event.entityA, event.entityB} s.currentCollisions[pair] = struct{}{} + displacement := event.normal.Scale(event.depth * (0.5)) + pos := vectors.Vec2{X: event.posX, Y: event.posY}.Sub(displacement) if _, exists := s.activeCollisions[pair]; !exists { proxy := s.EntityManager.Create() - s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) - s.Positions.Create(proxy, stdcomponents.Position{X: event.posX, Y: event.posY}) + s.Collisions.Create(proxy, stdcomponents.Collision{ + E1: pair.E1, + E2: pair.E2, + State: stdcomponents.CollisionStateEnter, + Normal: event.normal, + Depth: event.depth, + }) + + s.Positions.Create(proxy, stdcomponents.Position{X: pos.X, Y: pos.Y}) s.activeCollisions[pair] = proxy } else { proxy := s.activeCollisions[pair] - s.Collisions.Get(proxy).State = stdcomponents.CollisionStateStay - s.Positions.Get(proxy).X = event.posX - s.Positions.Get(proxy).Y = event.posY + collision := s.Collisions.Get(proxy) + position := s.Positions.Get(proxy) + collision.State = stdcomponents.CollisionStateStay + collision.Depth = event.depth + collision.Normal = event.normal + position.X = pos.X + position.Y = pos.Y } } close(doneChan) @@ -175,24 +194,15 @@ func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, } colliderB := s.GenericCollider.Get(entityB) - - if s.checkCollisionGjk(*colliderA, *colliderB, entityA, entityB) { - posA := s.Positions.Get(entityA) - posB := s.Positions.Get(entityB) - posX := (posA.X + posB.X) / 2 - posY := (posA.Y + posB.Y) / 2 - collisionChan <- CollisionEvent{ - entityA: entityA, - entityB: entityB, - posX: posX, - posY: posY, - } + collision, ok := s.checkCollisionGjk(*colliderA, *colliderB, entityA, entityB) + if ok { + collisionChan <- collision } }) } } -func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB stdcomponents.GenericCollider, entityA, entityB ecs.Entity) bool { +func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB stdcomponents.GenericCollider, entityA, entityB ecs.Entity) (e CollisionEvent, ok bool) { posA := s.Positions.Get(entityA) posB := s.Positions.Get(entityB) scaleA := s.getScaleOrDefault(entityA) @@ -204,7 +214,22 @@ func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB std supportA := s.getSupportFunction(entityA, colliderA, posA, &rotA, scaleA) supportB := s.getSupportFunction(entityB, colliderB, posB, &rotB, scaleB) - return s.gjkCollides(supportA, supportB) + // First detect collision using GJK + collision, simplex := s.gjkCollides(supportA, supportB) + if !collision { + return e, false + } + + // If collision detected, get penetration details using EPA + normal, depth := s.epa(simplex, supportA, supportB) + return CollisionEvent{ + entityA: entityA, + entityB: entityB, + posX: posA.X, + posY: posA.Y, + normal: normal, + depth: depth, + }, true } func (s *CollisionDetectionBVHSystem) checkCollision(colliderA, colliderB stdcomponents.GenericCollider, entityA, entityB ecs.Entity) bool { @@ -382,20 +407,21 @@ func (s *CollisionDetectionBVHSystem) polygonSupport(poly *stdcomponents.Polygon return maxVertex } -func (s *CollisionDetectionBVHSystem) gjkCollides(supportA, supportB func(vectors.Vec2) vectors.Vec2) bool { +func (s *CollisionDetectionBVHSystem) gjkCollides(supportA, supportB func(vectors.Vec2) vectors.Vec2) (bool, []vectors.Vec2) { direction := vectors.Vec2{X: 1, Y: 0} // Initial direction simplex := []vectors.Vec2{} + for i := 0; i < 50; i++ { // Max iterations to prevent infinite loop p := s.minkowskiSupport(supportA, supportB, direction) if p.Dot(direction) < 0 { - return false // No collision + return false, nil // No collision } simplex = append(simplex, p) if s.containsOrigin(simplex, direction) { - return true + return true, simplex } } - return false + return false, nil } func (s *CollisionDetectionBVHSystem) minkowskiSupport(supportA, supportB func(vectors.Vec2) vectors.Vec2, d vectors.Vec2) vectors.Vec2 { @@ -465,3 +491,76 @@ func (s *CollisionDetectionBVHSystem) tripleProduct(a, b, c vectors.Vec2) vector Y: b.Y*ac - a.Y*bc, } } + +func (s *CollisionDetectionBVHSystem) epa(simplex []vectors.Vec2, supportA, supportB func(vectors.Vec2) vectors.Vec2) (vectors.Vec2, float32) { + if len(simplex) < 3 { + return vectors.Vec2{}, 0 // Not a valid simplex + } + + polytope := make([]vectors.Vec2, len(simplex)) + copy(polytope, simplex) + + for iteration := 0; iteration < EPA_MAX_ITERATIONS; iteration++ { + edge, normal := s.findClosestEdge(polytope) + if normal.LengthSquared() < MIN_NORMAL_LENGTH { // Add safety check + return vectors.Vec2{}, 0 + } + + point := s.minkowskiSupport(supportA, supportB, normal) + if point.Dot(normal) < 0 { // Origin not in expansion direction + return vectors.Vec2{}, 0 + } + + distance := point.Dot(normal) + if math.Abs(float64(distance-edge.distance)) < EPA_TOLERANCE { + if normal.LengthSquared() < MIN_NORMAL_LENGTH { + return vectors.Vec2{}, 0 + } + return normal.NormalizedSafe(), distance + } + + polytope = s.insertPoint(polytope, edge.index, point) + } + return vectors.Vec2{}, 0 +} + +func (s *CollisionDetectionBVHSystem) findClosestEdge(polytope []vectors.Vec2) (СlosestEdge, vectors.Vec2) { + minDistance := float32(math.MaxFloat32) + bestEdge := СlosestEdge{index: -1} + var bestNormal vectors.Vec2 + + for i := 0; i < len(polytope); i++ { + j := (i + 1) % len(polytope) + a := polytope[i] + b := polytope[j] + + edge := b.Sub(a) + if edge.LengthSquared() < MIN_NORMAL_LENGTH { // Skip degenerate edges + continue + } + + normal := vectors.Vec2{-edge.Y, edge.X}.NormalizedSafe() + distance := normal.Dot(a) + + if distance < minDistance { + minDistance = distance + bestEdge = СlosestEdge{index: i, distance: distance} + bestNormal = normal + } + } + + return bestEdge, bestNormal +} + +type СlosestEdge struct { + index int + distance float32 +} + +func (s *CollisionDetectionBVHSystem) insertPoint(polytope []vectors.Vec2, index int, point vectors.Vec2) []vectors.Vec2 { + newPolytope := make([]vectors.Vec2, 0, len(polytope)+1) + newPolytope = append(newPolytope, polytope[:index+1]...) + newPolytope = append(newPolytope, point) + newPolytope = append(newPolytope, polytope[index+1:]...) + return newPolytope +} diff --git a/stdsystems/collision-detection-grid.go b/stdsystems/collision-detection-grid.go index a7acee3c..5c19d910 100644 --- a/stdsystems/collision-detection-grid.go +++ b/stdsystems/collision-detection-grid.go @@ -18,6 +18,7 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" + "gomp/vectors" "runtime" "sync" "time" @@ -73,6 +74,8 @@ func (s *CollisionDetectionGridSystem) Init() { type CollisionEvent struct { entityA, entityB ecs.Entity posX, posY float32 + normal vectors.Vec2 + depth float32 } func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { @@ -283,7 +286,7 @@ func (s *CollisionDetectionGridSystem) boxToXCollision(entityA ecs.Entity, colli posX := (position1.X + position2.X) / 2 posY := (position1.Y + position2.Y) / 2 - collisionChan <- CollisionEvent{entityA, entityB, posX, posY} + collisionChan <- CollisionEvent{entityA, entityB, posX, posY, vectors.Vec2{posX, posY}, 0} case stdcomponents.CircleColliderShape: panic("Circle-Box collision not implemented") default: diff --git a/vectors/vector2.go b/vectors/vector2.go index e01a81ca..09373e3d 100644 --- a/vectors/vector2.go +++ b/vectors/vector2.go @@ -75,3 +75,16 @@ func (v Vec2) Neg() Vec2 { func (v Vec2) Dot(other Vec2) float32 { return v.X*other.X + v.Y*other.Y } + +const ( + MIN_NORMAL_LENGTH = 0.00001 +) + +// NormalizedSafe is safe normalization method +func (v Vec2) NormalizedSafe() Vec2 { + lengthSq := v.X*v.X + v.Y*v.Y + if lengthSq < MIN_NORMAL_LENGTH { + return Vec2{1, 0} // Default to right direction + } + return v.Scale(float32(1 / math.Sqrt(float64(lengthSq)))) +} From 7301dd7930ba5a59d6eb47b9730672c50aa299e7 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 20 Mar 2025 21:11:47 +0300 Subject: [PATCH 054/196] collision-detection-bvh.go fix bvh and epa for 2d space --- examples/new-api/assets/satellite_B.png | Bin 0 -> 736 bytes examples/new-api/entities/asteroid.go | 16 +- examples/new-api/entities/bullet.go | 16 +- examples/new-api/entities/satellite.go | 79 +++++++ examples/new-api/entities/spaceship.go | 22 +- examples/new-api/entities/wall.go | 16 +- examples/new-api/instances/component-list.go | 2 + examples/new-api/instances/system-list.go | 12 +- examples/new-api/scenes/assterodd-scene.go | 3 + examples/new-api/systems/asterodd.go | 19 +- examples/new-api/systems/render-assterodd.go | 28 +-- stdcomponents/colliders.go | 32 ++- stdcomponents/ids.go | 1 + stdcomponents/position.go | 9 +- stdcomponents/rigidbody.go | 28 +++ stdsystems/collider.go | 32 +-- stdsystems/collision-detection-bvh.go | 221 +++++++++++-------- stdsystems/collision-detection-grid.go | 22 +- stdsystems/collision-reslution.go | 68 ++++++ stdsystems/ysort.go | 3 +- vectors/vector2.go | 7 + 21 files changed, 450 insertions(+), 186 deletions(-) create mode 100644 examples/new-api/assets/satellite_B.png create mode 100644 examples/new-api/entities/satellite.go create mode 100644 stdcomponents/rigidbody.go create mode 100644 stdsystems/collision-reslution.go diff --git a/examples/new-api/assets/satellite_B.png b/examples/new-api/assets/satellite_B.png new file mode 100644 index 0000000000000000000000000000000000000000..1d9840415f527d0025154ff51b3d9768ceac25c0 GIT binary patch literal 736 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!SkfJR9T^xl_H+M9WCikH1^9%x ze){y~%a?B-KYn@t{u2-Z*{@%J{PN}3t5+W`T)1=o{H+TY?!J2U{?VhCPoBJf`0&+} zCvRT7cnd_gZ$G(p>&e}_?{40Fa{c;)8#f+ZxpM#AyZ@Ii-F^4&-PNlP|KkNf`+=4M zt+;#l>4giofBpp8`2OL;7oR?TefRFun>QaXUcCGM{pXi2-@kbA4rtxw%lCjLKYsk` z>C@LwpT2qa?9J=fA09k-0krh$)dzR)KD%@0DcDJmZ{B=-?b?Iu*B=6%1a$WO`_Ipx zzYTN?S2RN>&{oZoAiv=F`~COh`}^bX&!0aZ$c_i%{`>quay~y0+Mj>FK0hAFzRyrt zlU>BXz?kFd;usQf`0e%Ud50VXSOYFqzb`SUxa_>SGq~Z4K+*sI<(qmNQcj*a_e3#h zNB(B1x$W7>v#%H$ujKrY?XZt!c931B?Y{IzrhUO4OpmI?4n$09_;HWrhj_z#W5)bO z&X8v_8fH9Vw9B&N{t)23v4P8PS!9Dlb{q2#ht*Tl7~}%hpEqjYx{z_kn1Qjxs=LG? zOrgZi@8m+`1nmR6Pc|9{Y`r|&^W0yp zh=-2q?mrDB7cMVysg}!l7^UW1yFyE`YZ1@ivns7=#jT&K);}ug{N5W9`|qatB#F7* wOZd maxDistance { + maxDistance = distance + maxPoint = vertices[i] + } } - // Rotate back to world space and translate - worldPoint := vectors.Vec2{X: localX, Y: localY}.Rotate(rot.Angle) - return vectors.Vec2{ - X: pos.X + worldPoint.X, - Y: pos.Y + worldPoint.Y, - } + return maxPoint } func (s *CollisionDetectionBVHSystem) polygonSupport(poly *stdcomponents.PolygonCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { @@ -409,19 +407,24 @@ func (s *CollisionDetectionBVHSystem) polygonSupport(poly *stdcomponents.Polygon func (s *CollisionDetectionBVHSystem) gjkCollides(supportA, supportB func(vectors.Vec2) vectors.Vec2) (bool, []vectors.Vec2) { direction := vectors.Vec2{X: 1, Y: 0} // Initial direction - simplex := []vectors.Vec2{} + simplex := []vectors.Vec2{s.minkowskiSupport(supportA, supportB, direction)} - for i := 0; i < 50; i++ { // Max iterations to prevent infinite loop + for { // Max iterations to prevent infinite loop p := s.minkowskiSupport(supportA, supportB, direction) + if p.X == 0 && p.Y == 0 { + return false, simplex // No collision + } + if p.Dot(direction) < 0 { return false, nil // No collision } simplex = append(simplex, p) - if s.containsOrigin(simplex, direction) { + ok, newSimplex := s.containsOrigin(simplex, &direction) + simplex = newSimplex + if ok { return true, simplex } } - return false, nil } func (s *CollisionDetectionBVHSystem) minkowskiSupport(supportA, supportB func(vectors.Vec2) vectors.Vec2, d vectors.Vec2) vectors.Vec2 { @@ -430,7 +433,7 @@ func (s *CollisionDetectionBVHSystem) minkowskiSupport(supportA, supportB func(v return a.Sub(b) } -func (s *CollisionDetectionBVHSystem) containsOrigin(simplex []vectors.Vec2, direction vectors.Vec2) bool { +func (s *CollisionDetectionBVHSystem) containsOrigin(simplex []vectors.Vec2, direction *vectors.Vec2) (bool, []vectors.Vec2) { a := (simplex)[len(simplex)-1] // Last point added ao := a.Neg() // Vector from A to origin @@ -449,36 +452,36 @@ func (s *CollisionDetectionBVHSystem) containsOrigin(simplex []vectors.Vec2, dir // Region AB if abPerp.Dot(ao) > 0 { simplex = []vectors.Vec2{a, b} - direction = abPerp - return false + *direction = abPerp + return false, simplex } // Region AC if acPerp.Dot(ao) > 0 { simplex = []vectors.Vec2{a, c} - direction = acPerp - return false + *direction = acPerp + return false, simplex } // Inside triangle - return true + return true, simplex case 2: // Line segment case b := (simplex)[0] ab := b.Sub(a) // Perpendicular to AB facing origin - abPerp := s.tripleProduct(ab, ao, ab) + abPerp := ab.Perpendicular() if abPerp.Dot(ao) > 0 { - direction = abPerp + *direction = abPerp } else { simplex = []vectors.Vec2{a} - direction = ao + *direction = ao } - return false + return false, simplex default: - return false + return false, simplex } } @@ -492,60 +495,112 @@ func (s *CollisionDetectionBVHSystem) tripleProduct(a, b, c vectors.Vec2) vector } } -func (s *CollisionDetectionBVHSystem) epa(simplex []vectors.Vec2, supportA, supportB func(vectors.Vec2) vectors.Vec2) (vectors.Vec2, float32) { - if len(simplex) < 3 { - return vectors.Vec2{}, 0 // Not a valid simplex +/* + function EPA(polytope, shapeA, shapeB) { + let minIndex = 0; + let minDistance = Infinity; + let minNormal; + + while (minDistance == Infinity) { + for (let i = 0; i < polytope.length; i++) { + let j = (i + 1) % polytope.length; + + let vertexI = polytope[i].copy(); + let vertexJ = polytope[j].copy(); + + let ij = vertexJ.sub(vertexI); + + let normal = createVector(ij.y, -ij.x).normalize(); + let distance = normal.dot(vertexI); + + if (distance < 0) { + distance *= -1; + normal.mult(-1); + } + + if (distance < minDistance) { + minDistance = distance; + minNormal = normal; + minIndex = j; + } + } + let support = support(shapeA, shapeB, minNormal); + let sDistance = minNormal.dot(support); + + if (abs(sDistance - minDistance) > 0.001) { + minDistance = Infinity; + polytope.splice(minIndex, 0, support); + } + } + + return minNormal.mult(minDistance + 0.001); } +*/ +func (s *CollisionDetectionBVHSystem) epa(simplex []vectors.Vec2, supportA, supportB func(vectors.Vec2) vectors.Vec2) (vectors.Vec2, float32) { + var minIndex int = 0 + var minDistance float32 = float32(math.MaxFloat32) + var minNormal vectors.Vec2 - polytope := make([]vectors.Vec2, len(simplex)) - copy(polytope, simplex) + for minDistance == float32(math.MaxFloat32) { + for i := 0; i < len(simplex); i++ { + j := (i + 1) % len(simplex) + a := simplex[i] + b := simplex[j] - for iteration := 0; iteration < EPA_MAX_ITERATIONS; iteration++ { - edge, normal := s.findClosestEdge(polytope) - if normal.LengthSquared() < MIN_NORMAL_LENGTH { // Add safety check - return vectors.Vec2{}, 0 - } + edge := b.Sub(a) - point := s.minkowskiSupport(supportA, supportB, normal) - if point.Dot(normal) < 0 { // Origin not in expansion direction - return vectors.Vec2{}, 0 - } + normal := vectors.Vec2{edge.Y, -edge.X}.Normalize() + distance := normal.Dot(a) - distance := point.Dot(normal) - if math.Abs(float64(distance-edge.distance)) < EPA_TOLERANCE { - if normal.LengthSquared() < MIN_NORMAL_LENGTH { - return vectors.Vec2{}, 0 + if distance < 0 { + distance *= -1 + normal = normal.Scale(-1) + } + + if distance < minDistance { + minDistance = distance + minNormal = normal + minIndex = j } - return normal.NormalizedSafe(), distance } - polytope = s.insertPoint(polytope, edge.index, point) + support := s.minkowskiSupport(supportA, supportB, minNormal) + sDistance := minNormal.Dot(support) + + if math.Abs(float64(sDistance-minDistance)) > EPA_TOLERANCE { + minDistance = float32(math.MaxFloat32) + simplex = append(simplex[:minIndex], append([]vectors.Vec2{support}, simplex[minIndex:]...)...) + } } - return vectors.Vec2{}, 0 + + return minNormal, minDistance + EPA_TOLERANCE } func (s *CollisionDetectionBVHSystem) findClosestEdge(polytope []vectors.Vec2) (СlosestEdge, vectors.Vec2) { - minDistance := float32(math.MaxFloat32) - bestEdge := СlosestEdge{index: -1} + bestEdge := СlosestEdge{index: -1, distance: float32(math.MaxFloat32)} var bestNormal vectors.Vec2 - for i := 0; i < len(polytope); i++ { - j := (i + 1) % len(polytope) - a := polytope[i] - b := polytope[j] + for bestEdge.distance == float32(math.MaxFloat32) { + for i := 0; i < len(polytope); i++ { + j := (i + 1) % len(polytope) + a := polytope[i] + b := polytope[j] - edge := b.Sub(a) - if edge.LengthSquared() < MIN_NORMAL_LENGTH { // Skip degenerate edges - continue - } + edge := b.Sub(a) + + normal := vectors.Vec2{edge.Y, -edge.X}.Normalize() + distance := normal.Dot(a) - normal := vectors.Vec2{-edge.Y, edge.X}.NormalizedSafe() - distance := normal.Dot(a) + if distance < 0 { + distance *= -1 + normal = normal.Neg() + } - if distance < minDistance { - minDistance = distance - bestEdge = СlosestEdge{index: i, distance: distance} - bestNormal = normal + if distance < bestEdge.distance { + bestEdge.distance = distance + bestEdge.index = j + bestNormal = normal + } } } @@ -556,11 +611,3 @@ type СlosestEdge struct { index int distance float32 } - -func (s *CollisionDetectionBVHSystem) insertPoint(polytope []vectors.Vec2, index int, point vectors.Vec2) []vectors.Vec2 { - newPolytope := make([]vectors.Vec2, 0, len(polytope)+1) - newPolytope = append(newPolytope, polytope[:index+1]...) - newPolytope = append(newPolytope, point) - newPolytope = append(newPolytope, polytope[index+1:]...) - return newPolytope -} diff --git a/stdsystems/collision-detection-grid.go b/stdsystems/collision-detection-grid.go index 5c19d910..b35c09fe 100644 --- a/stdsystems/collision-detection-grid.go +++ b/stdsystems/collision-detection-grid.go @@ -73,7 +73,7 @@ func (s *CollisionDetectionGridSystem) Init() { type CollisionEvent struct { entityA, entityB ecs.Entity - posX, posY float32 + position vectors.Vec2 normal vectors.Vec2 depth float32 } @@ -94,8 +94,8 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { scale := s.Scales.Get(entity) collider := s.GenericCollider.Get(entity) - cellX := int(position.X-(collider.OffsetX*scale.X)) / s.cellSizeX - cellY := int(position.Y-(collider.OffsetY*scale.Y)) / s.cellSizeY + cellX := int(position.X-(collider.Offset.X*scale.X)) / s.cellSizeX + cellY := int(position.Y-(collider.Offset.Y*scale.Y)) / s.cellSizeY cell := stdcomponents.SpatialIndex{X: cellX, Y: cellY} s.entityToCell[entity] = cell s.spatialBuckets[cell] = append(s.spatialBuckets[cell], entity) @@ -109,10 +109,10 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { collider := s.BoxColliders.Get(entity) scale := s.Scales.Get(entity) newAABB := aabb{ - Left: position.X - (collider.OffsetX * scale.X), - Right: position.X + (collider.Width-collider.OffsetX)*scale.X, - Top: position.Y - (collider.OffsetY * scale.Y), - Bottom: position.Y + (collider.Height-collider.OffsetY)*scale.Y, + Left: position.X - (collider.Offset.X * scale.X), + Right: position.X + (collider.Width-collider.Offset.X)*scale.X, + Top: position.Y - (collider.Offset.Y * scale.Y), + Bottom: position.Y + (collider.Height-collider.Offset.Y)*scale.Y, } s.aabbs[entity] = newAABB return true @@ -131,13 +131,13 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { if _, exists := s.activeCollisions[pair]; !exists { proxy := s.EntityManager.Create() s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) - s.Positions.Create(proxy, stdcomponents.Position{X: event.posX, Y: event.posY}) + s.Positions.Create(proxy, stdcomponents.Position{X: event.position.X, Y: event.position.Y}) s.activeCollisions[pair] = proxy } else { proxy := s.activeCollisions[pair] s.Collisions.Get(proxy).State = stdcomponents.CollisionStateStay - s.Positions.Get(proxy).X = event.posX - s.Positions.Get(proxy).Y = event.posY + s.Positions.Get(proxy).X = event.position.X + s.Positions.Get(proxy).Y = event.position.Y } } close(doneChan) @@ -286,7 +286,7 @@ func (s *CollisionDetectionGridSystem) boxToXCollision(entityA ecs.Entity, colli posX := (position1.X + position2.X) / 2 posY := (position1.Y + position2.Y) / 2 - collisionChan <- CollisionEvent{entityA, entityB, posX, posY, vectors.Vec2{posX, posY}, 0} + collisionChan <- CollisionEvent{entityA, entityB, vectors.Vec2{posX, posY}, vectors.Vec2{posX, posY}, 0} case stdcomponents.CircleColliderShape: panic("Circle-Box collision not implemented") default: diff --git a/stdsystems/collision-reslution.go b/stdsystems/collision-reslution.go new file mode 100644 index 00000000..83aefac3 --- /dev/null +++ b/stdsystems/collision-reslution.go @@ -0,0 +1,68 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "log" + "time" +) + +func NewCollisionResolutionSystem() CollisionResolutionSystem { + return CollisionResolutionSystem{} +} + +type CollisionResolutionSystem struct { + EntityManager *ecs.EntityManager + Collisions *stdcomponents.CollisionComponentManager + Positions *stdcomponents.PositionComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager +} + +func (s *CollisionResolutionSystem) Init() {} +func (s *CollisionResolutionSystem) Run(dt time.Duration) { + s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { + if collision.State == stdcomponents.CollisionStateEnter || collision.State == stdcomponents.CollisionStateStay { + log.Printf("Collision resolution: %v", collision) + // Resolve penetration + displacement := collision.Normal.Scale(collision.Depth * 0.5) + + // Move entities apart + rigidbody1 := s.RigidBodies.Get(collision.E1) + rigidbody2 := s.RigidBodies.Get(collision.E2) + + if rigidbody1 == nil || rigidbody2 == nil { + return true + } + + if !rigidbody1.IsStatic { + p1 := s.Positions.Get(collision.E1) + p1d := p1.Sub(displacement) + p1.X, p1.Y = p1d.X, p1d.Y + } + + if !rigidbody2.IsStatic { + p2 := s.Positions.Get(collision.E2) + p2d := p2.Add(displacement) + p2.X, p2.Y = p2d.X, p2d.Y + } + + // Apply forces or velocity changes + } + return true + }) +} +func (s *CollisionResolutionSystem) Destroy() {} diff --git a/stdsystems/ysort.go b/stdsystems/ysort.go index 4875b242..2876b421 100644 --- a/stdsystems/ysort.go +++ b/stdsystems/ysort.go @@ -42,7 +42,8 @@ func (s *YSortSystem) Run() { yDepth := pos.Y * ySortOffsetScale // Preserve original Z layer but add Y-based offset - renderOrder.CalculatedZ = float32(int(pos.Z)) + yDepth + //renderOrder.CalculatedZ = float32(int(pos.Z)) + yDepth + renderOrder.CalculatedZ = yDepth return true }) diff --git a/vectors/vector2.go b/vectors/vector2.go index 09373e3d..af14f000 100644 --- a/vectors/vector2.go +++ b/vectors/vector2.go @@ -72,6 +72,13 @@ func (v Vec2) Neg() Vec2 { return Vec2{-v.X, -v.Y} } +func (v Vec2) Perpendicular() Vec2 { + return Vec2{ + X: -v.Y, + Y: v.X, + } +} + func (v Vec2) Dot(other Vec2) float32 { return v.X*other.X + v.Y*other.Y } From 0f3af9e1ee2eaaa658d715fd327a9cbb180af2c2 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 20 Mar 2025 22:29:43 +0300 Subject: [PATCH 055/196] collision-detection-bvh.go fix tp issue --- engine.go | 2 +- examples/new-api/systems/render-assterodd.go | 2 +- stdsystems/collision-detection-bvh.go | 21 +++++++++++++------- stdsystems/collision-reslution.go | 2 -- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/engine.go b/engine.go index 476e5751..7a29b6e8 100644 --- a/engine.go +++ b/engine.go @@ -21,7 +21,7 @@ import ( ) const ( - MaxFrameSkips = 50 + MaxFrameSkips = 5 ) func NewEngine(game AnyGame) Engine { diff --git a/examples/new-api/systems/render-assterodd.go b/examples/new-api/systems/render-assterodd.go index d7e0074e..c5969855 100644 --- a/examples/new-api/systems/render-assterodd.go +++ b/examples/new-api/systems/render-assterodd.go @@ -161,7 +161,7 @@ func (s *RenderAssteroddSystem) render() { rl.BeginMode2D(s.camera) s.Collisions.EachEntity(func(entity ecs.Entity) bool { pos := s.Positions.Get(entity) - rl.DrawRectangle(int32(pos.X), int32(pos.Y), 16, 16, rl.Red) + rl.DrawRectangle(int32(pos.X-8), int32(pos.Y-8), 16, 16, rl.Red) return true }) s.ColliderBoxes.EachEntity(func(e ecs.Entity) bool { diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index bfdaa311..6d159241 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -222,7 +222,7 @@ func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB std // If collision detected, get penetration details using EPA normal, depth := s.epa(simplex, supportA, supportB) - position := posA.Add(posB.Sub(*posA).Scale(0.5)) + position := posA.Add(posB.Sub(*posA)) return CollisionEvent{ entityA: entityA, entityB: entityB, @@ -367,21 +367,28 @@ func (s *CollisionDetectionBVHSystem) circleSupport(circle *stdcomponents.Circle } func (s *CollisionDetectionBVHSystem) boxSupport(box *stdcomponents.BoxCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { + halfWidth := box.Width * scale.X / 2 + halfHeight := box.Height * scale.Y / 2 vertices := [4]vectors.Vec2{ - pos.Add(vectors.Vec2{X: box.Width * scale.X / 2, Y: box.Height * scale.Y / 2}), - pos.Add(vectors.Vec2{X: -box.Width * scale.X / 2, Y: box.Height * scale.Y / 2}), - pos.Add(vectors.Vec2{X: -box.Width * scale.X / 2, Y: -box.Height * scale.Y / 2}), - pos.Add(vectors.Vec2{X: box.Width * scale.X / 2, Y: -box.Height * scale.Y / 2}), + {X: halfWidth, Y: halfHeight}, + {X: -halfWidth, Y: halfHeight}, + {X: -halfWidth, Y: -halfHeight}, + {X: halfWidth, Y: -halfHeight}, } var maxPoint vectors.Vec2 var maxDistance float32 = -math.MaxFloat32 for i := range vertices { - distance := vertices[i].Dot(direction) + // Apply rotation + rotated := vertices[i].Rotate(rot.Angle) + // Move to world space + worldVertex := vectors.Vec2{X: pos.X + rotated.X, Y: pos.Y + rotated.Y} + + distance := worldVertex.Dot(direction) if distance > maxDistance { maxDistance = distance - maxPoint = vertices[i] + maxPoint = worldVertex } } diff --git a/stdsystems/collision-reslution.go b/stdsystems/collision-reslution.go index 83aefac3..f43aa8b3 100644 --- a/stdsystems/collision-reslution.go +++ b/stdsystems/collision-reslution.go @@ -17,7 +17,6 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" - "log" "time" ) @@ -36,7 +35,6 @@ func (s *CollisionResolutionSystem) Init() {} func (s *CollisionResolutionSystem) Run(dt time.Duration) { s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { if collision.State == stdcomponents.CollisionStateEnter || collision.State == stdcomponents.CollisionStateStay { - log.Printf("Collision resolution: %v", collision) // Resolve penetration displacement := collision.Normal.Scale(collision.Depth * 0.5) From 967a54617eb55a8e0450e7868839f2150b6ab910 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sat, 22 Mar 2025 20:35:23 +0300 Subject: [PATCH 056/196] collision-detection-bvh.go 4 hours of aabb size fixing, please help --- examples/new-api/entities/asteroid.go | 30 +++-- examples/new-api/entities/bullet.go | 25 +++-- examples/new-api/entities/player.go | 13 ++- examples/new-api/entities/satellite.go | 26 +++-- examples/new-api/entities/space-spawner.go | 7 +- examples/new-api/entities/spaceship.go | 24 ++-- examples/new-api/entities/wall.go | 37 +++--- examples/new-api/game.go | 7 +- examples/new-api/systems/asterodd.go | 45 ++------ examples/new-api/systems/collision-handler.go | 44 ++++---- examples/new-api/systems/player.go | 8 +- examples/new-api/systems/render-assterodd.go | 62 ++++++---- examples/new-api/systems/render-bogdan.go | 14 +-- examples/new-api/systems/space-spawner.go | 6 +- examples/new-api/systems/spaceship-intents.go | 38 +++---- stdcomponents/colliders.go | 3 +- stdcomponents/position.go | 4 +- stdcomponents/rotation.go | 17 ++- stdcomponents/scale.go | 7 +- stdsystems/collider.go | 37 +++--- stdsystems/collision-detection-bvh.go | 106 ++++-------------- stdsystems/collision-detection-grid.go | 33 +++--- stdsystems/collision-reslution.go | 19 +++- stdsystems/network-send.go | 2 +- stdsystems/sprite-matrix.go | 6 +- stdsystems/sprite.go | 10 +- stdsystems/velocity.go | 4 +- stdsystems/ysort.go | 2 +- vectors/radian.go | 17 +++ vectors/vector2.go | 23 +--- 30 files changed, 341 insertions(+), 335 deletions(-) create mode 100644 vectors/radian.go diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index 8e41be42..2b4e003a 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -36,33 +36,39 @@ type CreateAsteroidManagers struct { Sprites *stdcomponents.SpriteComponentManager AsteroidTags *components.AsteroidComponentManager Hp *components.HpComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager } func CreateAsteroid( props CreateAsteroidManagers, - posX, posY, angle float32, + posX, posY float32, + angle float64, scaleFactor float32, velocityX, velocityY float32, ) ecs.Entity { bullet := props.EntityManager.Create() props.Positions.Create(bullet, stdcomponents.Position{ - X: posX, - Y: posY, - }) - props.Rotations.Create(bullet, stdcomponents.Rotation{ - Angle: angle, + XY: vectors.Vec2{ + X: posX, + Y: posY, + }, }) + props.Rotations.Create(bullet, stdcomponents.Rotation{}.SetFromDegrees(angle)) props.Scales.Create(bullet, stdcomponents.Scale{ - X: 1 * scaleFactor, - Y: 1 * scaleFactor, + XY: vectors.Vec2{ + X: 1 * scaleFactor, + Y: 1 * scaleFactor, + }, }) props.Velocities.Create(bullet, stdcomponents.Velocity{ X: velocityX, Y: velocityY, }) props.BoxColliders.Create(bullet, stdcomponents.BoxCollider{ - Width: 32, - Height: 32, + WH: vectors.Vec2{ + X: 32, + Y: 32, + }, Offset: vectors.Vec2{ X: 16, Y: 16, @@ -95,6 +101,10 @@ func CreateAsteroid( Hp: hp, MaxHp: hp, }) + props.RigidBodies.Create(bullet, stdcomponents.RigidBody{ + IsStatic: true, + Mass: 1, + }) return bullet } diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 361fa400..5c51c841 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -39,28 +39,33 @@ type CreateBulletManagers struct { func CreateBullet( props CreateBulletManagers, - posX, posY, angle float32, + posX, posY float32, + angle float64, velocityX, velocityY float32, ) ecs.Entity { bullet := props.EntityManager.Create() props.Positions.Create(bullet, stdcomponents.Position{ - X: posX, - Y: posY, - }) - props.Rotations.Create(bullet, stdcomponents.Rotation{ - Angle: angle, + XY: vectors.Vec2{ + X: posX, + Y: posY, + }, }) + props.Rotations.Create(bullet, stdcomponents.Rotation{}.SetFromDegrees(angle)) props.Scales.Create(bullet, stdcomponents.Scale{ - X: 1, - Y: 1, + XY: vectors.Vec2{ + X: 1, + Y: 1, + }, }) props.Velocities.Create(bullet, stdcomponents.Velocity{ X: velocityX, Y: velocityY, }) props.BoxColliders.Create(bullet, stdcomponents.BoxCollider{ - Width: 16, - Height: 16, + WH: vectors.Vec2{ + X: 16, + Y: 16, + }, Offset: vectors.Vec2{ X: 8, Y: 8, diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index 05ff1297..19ffd72f 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -11,6 +11,7 @@ import ( "gomp/examples/new-api/sprites" "gomp/pkg/ecs" "gomp/stdcomponents" + "gomp/vectors" ) const ( @@ -73,8 +74,10 @@ func CreatePlayer( // Adding scale component scale := stdcomponents.Scale{ - X: 1, - Y: 1, + XY: vectors.Vec2{ + X: 1, + Y: 1, + }, } player.Scale = scales.Create(entity, scale) @@ -110,8 +113,10 @@ func CreatePlayer( // Adding BoxCollider component player.ColliderBox = boxColliders.Create(entity, stdcomponents.BoxCollider{ - Width: 96, - Height: 128, + WH: vectors.Vec2{ + X: 96, + Y: 128, + }, }) // Adding GenericCollider component diff --git a/examples/new-api/entities/satellite.go b/examples/new-api/entities/satellite.go index 52213390..6f9cd774 100644 --- a/examples/new-api/entities/satellite.go +++ b/examples/new-api/entities/satellite.go @@ -37,21 +37,23 @@ type CreateSatelliteManagers struct { func CreateSatellite( props CreateSatelliteManagers, posX, posY float32, - rotationAngle float32, + angle float64, ) ecs.Entity { satellite := props.EntityManager.Create() props.Positions.Create(satellite, stdcomponents.Position{ - X: posX, - Y: posY, + XY: vectors.Vec2{ + X: posX, + Y: posY, + }, }) - props.Rotations.Create(satellite, stdcomponents.Rotation{ - Angle: rotationAngle, - }) + props.Rotations.Create(satellite, stdcomponents.Rotation{}.SetFromDegrees(angle)) props.Scales.Create(satellite, stdcomponents.Scale{ - X: 1, - Y: 1, + XY: vectors.Vec2{ + X: 1, + Y: 1, + }, }) props.Sprites.Create(satellite, stdcomponents.Sprite{ @@ -62,8 +64,10 @@ func CreateSatellite( }) props.BoxColliders.Create(satellite, stdcomponents.BoxCollider{ - Width: 64, - Height: 64, + WH: vectors.Vec2{ + X: 64, + Y: 64, + }, Offset: vectors.Vec2{ X: 32, Y: 32, @@ -72,7 +76,7 @@ func CreateSatellite( Mask: 1< 5000 { + if pos.XY.Y > 5000 { s.EntityManager.Delete(e) } @@ -64,7 +64,7 @@ func (s *CollisionHandlerSystem) Run(dt time.Duration) { s.BulletTags.EachEntity(func(entity ecs.Entity) bool { pos := s.Positions.Get(entity) - if pos.Y > 5000 || pos.Y < 0 || pos.X > 5000 || pos.X < 0 { + if pos.XY.Y > 5000 || pos.XY.Y < 0 || pos.XY.X > 5000 || pos.XY.X < 0 { s.EntityManager.Delete(entity) } return true @@ -102,16 +102,16 @@ func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bo return true } - wallTag := s.WallTags.Get(e2) - if wallTag != nil { - // reverse player movement vector - velocity := s.Velocities.Get(e1) - rotation := s.Rotations.Get(e1) - velocity.X *= -1 - velocity.Y *= -1 - rotation.Angle += 180 - return true - } + //wallTag := s.WallTags.Get(e2) + //if wallTag != nil { + // // reverse player movement vector + // velocity := s.Velocities.Get(e1) + // rotation := s.Rotations.Get(e1) + // velocity.X *= -1 + // velocity.Y *= -1 + // rotation.Angle += 180 + // return true + //} } else if e2Tag != nil { // this is a player asteroidTag := s.AsteroidTags.Get(e1) @@ -121,16 +121,16 @@ func (s *CollisionHandlerSystem) checkPlayerCollisionEnter(e1, e2 ecs.Entity) bo return true } - wallTag := s.WallTags.Get(e1) - if wallTag != nil { - // reverse player movement vector - velocity := s.Velocities.Get(e2) - rotation := s.Rotations.Get(e2) - velocity.X *= -1 - velocity.Y *= -1 - rotation.Angle += 180 - return true - } + //wallTag := s.WallTags.Get(e1) + //if wallTag != nil { + // // reverse player movement vector + // velocity := s.Velocities.Get(e2) + // rotation := s.Rotations.Get(e2) + // velocity.X *= -1 + // velocity.Y *= -1 + // rotation.Angle += 180 + // return true + //} } return false diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 60e26878..dd4c4a12 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -52,8 +52,8 @@ func (s *PlayerSystem) Init() { s.YSorts, s.RenderOrders, s.BoxColliders, s.GenericCollider, ) - npc.Position.X = 100 + rand.Float32()*700 - npc.Position.Y = 100 + rand.Float32()*500 + npc.Position.XY.X = 100 + rand.Float32()*700 + npc.Position.XY.Y = 100 + rand.Float32()*500 npc.AnimationPlayer.Current = uint8(rand.Intn(7)) } @@ -62,8 +62,8 @@ func (s *PlayerSystem) Init() { s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, s.Renderables, s.YSorts, s.RenderOrders, s.BoxColliders, s.GenericCollider, ) - player.Position.X = 100 - player.Position.Y = 100 + player.Position.XY.X = 100 + player.Position.XY.Y = 100 player.GenericCollider.Layer = config.PlayerCollisionLayer player.GenericCollider.Mask = 1 << config.EnemyCollisionLayer diff --git a/examples/new-api/systems/render-assterodd.go b/examples/new-api/systems/render-assterodd.go index c5969855..0e03b6ea 100644 --- a/examples/new-api/systems/render-assterodd.go +++ b/examples/new-api/systems/render-assterodd.go @@ -47,6 +47,7 @@ type RenderAssteroddSystem struct { SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager RenderOrders *stdcomponents.RenderOrderComponentManager ColliderBoxes *stdcomponents.BoxColliderComponentManager + AABBs *stdcomponents.AABBComponentManager Collisions *stdcomponents.CollisionComponentManager renderList []renderEntry instanceData []stdcomponents.RLTexturePro @@ -104,6 +105,28 @@ func (s *RenderAssteroddSystem) Run(dt time.Duration) bool { func (s *RenderAssteroddSystem) Destroy() {} func (s *RenderAssteroddSystem) render() { + // ========== + // DEBUG + // ========== + rl.BeginMode2D(s.camera) + s.ColliderBoxes.EachEntity(func(e ecs.Entity) bool { + col := s.ColliderBoxes.Get(e) + scale := s.Scales.Get(e) + pos := s.Positions.Get(e) + rot := s.Rotations.Get(e) + + rl.DrawRectanglePro(rl.Rectangle{ + X: pos.XY.X, + Y: pos.XY.Y, + Width: col.WH.X * scale.XY.X, + Height: col.WH.Y * scale.XY.Y, + }, rl.Vector2{ + X: col.Offset.X * scale.XY.X, + Y: col.Offset.Y * scale.XY.Y, + }, float32(rot.Degrees()), rl.DarkGreen) + return true + }) + rl.EndMode2D() // Extract and sort entities if cap(s.renderList) < s.Renderables.Len() { @@ -158,24 +181,23 @@ func (s *RenderAssteroddSystem) render() { s.submitBatch(currentTex, s.instanceData) // Submit last batch s.renderList = s.renderList[:0] + // ========== + // DEBUG + // ========== rl.BeginMode2D(s.camera) - s.Collisions.EachEntity(func(entity ecs.Entity) bool { - pos := s.Positions.Get(entity) - rl.DrawRectangle(int32(pos.X-8), int32(pos.Y-8), 16, 16, rl.Red) + s.AABBs.EachEntity(func(e ecs.Entity) bool { + aabb := s.AABBs.Get(e) + rl.DrawRectangleLines(int32(aabb.Min.X), int32(aabb.Min.Y), int32(aabb.Max.X-aabb.Min.X), int32(aabb.Max.Y-aabb.Min.Y), rl.Green) return true }) - s.ColliderBoxes.EachEntity(func(e ecs.Entity) bool { - box := s.ColliderBoxes.Get(e) - pos := s.Positions.Get(e) - scale := s.Scales.Get(e) - col := s.ColliderBoxes.Get(e) - - rl.DrawRectangleLines(int32(pos.X-(col.Offset.X*scale.X)), int32(pos.Y-(col.Offset.Y*scale.Y)), int32(box.Width*scale.X), int32(box.Height*scale.Y), rl.DarkGreen) + s.Collisions.EachEntity(func(entity ecs.Entity) bool { + pos := s.Positions.Get(entity) + rl.DrawRectangle(int32(pos.XY.X-8), int32(pos.XY.Y-8), 16, 16, rl.Red) return true }) s.Renderables.EachEntity(func(e ecs.Entity) bool { position := s.Positions.Get(e) - rl.DrawRectangle(int32(position.X-2), int32(position.Y-2), 4, 4, rl.Red) + rl.DrawRectangle(int32(position.XY.X-2), int32(position.XY.Y-2), 4, 4, rl.Red) return true }) rl.EndMode2D() @@ -227,14 +249,14 @@ func (s *RenderAssteroddSystem) prepareFlips(wg *sync.WaitGroup) { defer wg.Done() s.RlTexturePros.EachEntityParallel(func(entity ecs.Entity) bool { texturePro := s.RlTexturePros.Get(entity) - mirrored := s.Flips.Get(entity) - if mirrored == nil { + flipped := s.Flips.Get(entity) + if flipped == nil { return true } - if mirrored.X { + if flipped.X { texturePro.Frame.Width *= -1 } - if mirrored.Y { + if flipped.Y { texturePro.Frame.Height *= -1 } return true @@ -251,8 +273,8 @@ func (s *RenderAssteroddSystem) preparePositions(wg *sync.WaitGroup, dt time.Dur return true } decay := 40.0 // DECAY IS TICKRATE DEPENDENT - x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.X), decay, dts)) - y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.Y), decay, dts)) + x := float32(s.expDecay(float64(texturePro.Dest.X), float64(position.XY.X), decay, dts)) + y := float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.XY.Y), decay, dts)) texturePro.Dest.X = x texturePro.Dest.Y = y player := s.Player.Get(entity) @@ -273,7 +295,7 @@ func (s *RenderAssteroddSystem) prepareRotations(wg *sync.WaitGroup) { if rotation == nil { return true } - texturePro.Rotation = rotation.Angle + texturePro.Rotation = float32(rotation.Degrees()) return true }) } @@ -286,8 +308,8 @@ func (s *RenderAssteroddSystem) prepareScales(wg *sync.WaitGroup) { if scale == nil { return true } - texturePro.Dest.Width *= scale.X - texturePro.Dest.Height *= scale.Y + texturePro.Dest.Width *= scale.XY.X + texturePro.Dest.Height *= scale.XY.Y return true }) } diff --git a/examples/new-api/systems/render-bogdan.go b/examples/new-api/systems/render-bogdan.go index bd677652..dc605244 100644 --- a/examples/new-api/systems/render-bogdan.go +++ b/examples/new-api/systems/render-bogdan.go @@ -85,12 +85,12 @@ func (s *RenderBogdanSystem) Run(dt time.Duration) bool { box := s.ColliderBoxes.Get(e) pos := s.Positions.Get(e) - rl.DrawRectangleLines(int32(pos.X), int32(pos.Y), int32(box.Width), int32(box.Height), rl.Red) + rl.DrawRectangleLines(int32(pos.XY.X), int32(pos.XY.Y), int32(box.WH.X), int32(box.WH.Y), rl.Red) return true }) s.Collisions.EachEntity(func(entity ecs.Entity) bool { pos := s.Positions.Get(entity) - rl.DrawRectangle(int32(pos.X), int32(pos.Y), 16, 16, rl.Red) + rl.DrawRectangle(int32(pos.XY.X), int32(pos.XY.X), 16, 16, rl.Red) return true }) rl.DrawRectangle(0, 0, 200, 60, rl.DarkBrown) @@ -208,8 +208,8 @@ func (s *RenderBogdanSystem) preparePositions(wg *sync.WaitGroup, dt time.Durati //decay := 16.0 // DECAY IS TICKRATE DEPENDENT //texturePro.Dest.X = float32(s.expDecay(float64(texturePro.Dest.X), float64(position.X), decay, dts)) //texturePro.Dest.Y = float32(s.expDecay(float64(texturePro.Dest.Y), float64(position.Y), decay, dts)) - texturePro.Dest.X = position.X - texturePro.Dest.Y = position.Y + texturePro.Dest.X = position.XY.X + texturePro.Dest.Y = position.XY.Y return true }) @@ -223,7 +223,7 @@ func (s *RenderBogdanSystem) prepareRotations(wg *sync.WaitGroup) { if rotation == nil { return true } - texturePro.Rotation = rotation.Angle + texturePro.Rotation = float32(rotation.Angle) return true }) } @@ -236,8 +236,8 @@ func (s *RenderBogdanSystem) prepareScales(wg *sync.WaitGroup) { if scale == nil { return true } - texturePro.Dest.Width *= scale.X - texturePro.Dest.Height *= scale.Y + texturePro.Dest.Width *= scale.XY.X + texturePro.Dest.Height *= scale.XY.Y return true }) } diff --git a/examples/new-api/systems/space-spawner.go b/examples/new-api/systems/space-spawner.go index 005f144e..cdf6c8f9 100644 --- a/examples/new-api/systems/space-spawner.go +++ b/examples/new-api/systems/space-spawner.go @@ -38,6 +38,7 @@ type SpaceSpawnerSystem struct { Velocities *stdcomponents.VelocityComponentManager Rotations *stdcomponents.RotationComponentManager Scales *stdcomponents.ScaleComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager } func (s *SpaceSpawnerSystem) Init() {} @@ -46,7 +47,7 @@ func (s *SpaceSpawnerSystem) Run(dt time.Duration) { position := s.Positions.Get(e) velocity := s.Velocities.Get(e) - if position.X > 5000 || position.X < 0 { + if position.XY.X > 5000 || position.XY.X < 0 { velocity.X = -velocity.X } @@ -67,7 +68,8 @@ func (s *SpaceSpawnerSystem) Run(dt time.Duration) { Sprites: s.Sprites, AsteroidTags: s.Asteroids, Hp: s.Hp, - }, pos.X, pos.Y, 0, 1+rand.Float32()*2, 0, 50+rand.Float32()*100) + RigidBodies: s.RigidBodies, + }, pos.XY.X, pos.XY.Y, 0, 1+rand.Float32()*2, 0, 50+rand.Float32()*100) spawner.CooldownLeft = spawner.Cooldown return true }) diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index ca376a8c..39b87988 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -19,6 +19,7 @@ import ( "gomp/examples/new-api/entities" "gomp/pkg/ecs" "gomp/stdcomponents" + "gomp/vectors" "math" "time" ) @@ -46,11 +47,13 @@ func (s *SpaceshipIntentsSystem) Init() {} func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { var moveSpeedMax float32 = 300 var moveSpeedMaxBackwards float32 = -200 - var rotateSpeed float32 = 10 + var rotateSpeed vectors.Radians = 3 var speedIncrement float32 = 10 var bulletSpeed float32 = 300 + dtSec := float32(dt.Seconds()) + s.SpaceshipIntents.EachEntity(func(entity ecs.Entity) bool { intent := s.SpaceshipIntents.Get(entity) vel := s.Velocities.Get(entity) @@ -58,17 +61,11 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { pos := s.Positions.Get(entity) weapon := s.Weapons.Get(entity) - //if pos.Y < 0 || pos.Y > 5000 || pos.X < 0 || pos.X > 5000 { - // vel.X *= -1 - // vel.Y *= -1 - // rot.Angle += 180 - //} - if intent.RotateLeft { - rot.Angle -= rotateSpeed + rot.Angle -= rotateSpeed * vectors.Radians(dtSec) } if intent.RotateRight { - rot.Angle += rotateSpeed + rot.Angle += rotateSpeed * vectors.Radians(dtSec) } if intent.MoveUp { s.moveSpeed += speedIncrement @@ -91,22 +88,17 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { } } - rads := deg2rad(float64(rot.Angle)) + math.Pi - - vel.Y = float32(math.Cos(rads)) * s.moveSpeed - vel.X = -float32(math.Sin(rads)) * s.moveSpeed + vel.Y = float32(math.Cos(rot.Angle+math.Pi)) * s.moveSpeed + vel.X = -float32(math.Sin(rot.Angle+math.Pi)) * s.moveSpeed if weapon.CooldownLeft <= 0 { if intent.Fire { - count := 360 - ofs := 1 - startAngle := rot.Angle - float32(count*ofs/2) + var count int = 360 for i := range count { - angle := startAngle + float32(i*ofs) - rads = deg2rad(float64(angle)) + math.Pi + var angle = math.Pi*2/float64(count)*float64(i) + rot.Angle - math.Pi/2 - bulletVelocityY := vel.Y + float32(math.Cos(rads))*bulletSpeed - bulletVelocityX := vel.X - float32(math.Sin(rads))*bulletSpeed + bulletVelocityY := vel.Y + float32(math.Cos(angle+math.Pi))*bulletSpeed + bulletVelocityX := vel.X - float32(math.Sin(angle+math.Pi))*bulletSpeed entities.CreateBullet(entities.CreateBulletManagers{ EntityManager: s.EntityManager, Positions: s.Positions, @@ -117,7 +109,7 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { Sprites: s.Sprites, BulletTags: s.BulletTags, Hps: s.Hps, - }, pos.X, pos.Y, angle, bulletVelocityX, bulletVelocityY) + }, pos.XY.X, pos.XY.Y, angle, bulletVelocityX, bulletVelocityY) } weapon.CooldownLeft = weapon.Cooldown } @@ -130,7 +122,3 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { } func (s *SpaceshipIntentsSystem) Destroy() {} - -func deg2rad(deg float64) float64 { - return deg * math.Pi / 180 -} diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 2fcf83ae..2cc5d01b 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -41,8 +41,7 @@ const ( ) type BoxCollider struct { - Width float32 - Height float32 + WH vectors.Vec2 Offset vectors.Vec2 Layer CollisionLayer Mask CollisionMask diff --git a/stdcomponents/position.go b/stdcomponents/position.go index 39f78c70..d1bbed5b 100644 --- a/stdcomponents/position.go +++ b/stdcomponents/position.go @@ -19,7 +19,9 @@ import ( "gomp/vectors" ) -type Position = vectors.Vec2 +type Position struct { + XY vectors.Vec2 +} type PositionComponentManager = ecs.ComponentManager[Position] diff --git a/stdcomponents/rotation.go b/stdcomponents/rotation.go index b2133119..d8856886 100644 --- a/stdcomponents/rotation.go +++ b/stdcomponents/rotation.go @@ -14,10 +14,23 @@ Thank you for your support! package stdcomponents -import "gomp/pkg/ecs" +import ( + "gomp/pkg/ecs" + "gomp/vectors" + "math" +) type Rotation struct { - Angle float32 + Angle vectors.Radians +} + +func (r Rotation) SetFromDegrees(deg float64) Rotation { + r.Angle = deg * math.Pi / 180 + return r +} + +func (r Rotation) Degrees() float64 { + return r.Angle * 180 / math.Pi } type RotationComponentManager = ecs.ComponentManager[Rotation] diff --git a/stdcomponents/scale.go b/stdcomponents/scale.go index 13b1593f..9d2925bc 100644 --- a/stdcomponents/scale.go +++ b/stdcomponents/scale.go @@ -14,10 +14,13 @@ Thank you for your support! package stdcomponents -import "gomp/pkg/ecs" +import ( + "gomp/pkg/ecs" + "gomp/vectors" +) type Scale struct { - X, Y float32 + XY vectors.Vec2 } type ScaleComponentManager = ecs.ComponentManager[Scale] diff --git a/stdsystems/collider.go b/stdsystems/collider.go index dc14215d..84c9ddcf 100644 --- a/stdsystems/collider.go +++ b/stdsystems/collider.go @@ -14,6 +14,7 @@ Thank you for your support! package stdsystems +import "C" import ( "gomp/pkg/ecs" "gomp/stdcomponents" @@ -29,6 +30,7 @@ type ColliderSystem struct { EntityManager *ecs.EntityManager Positions *stdcomponents.PositionComponentManager Scales *stdcomponents.ScaleComponentManager + Rotations *stdcomponents.RotationComponentManager GenericColliders *stdcomponents.GenericColliderComponentManager BoxColliders *stdcomponents.BoxColliderComponentManager CircleColliders *stdcomponents.CircleColliderComponentManager @@ -52,18 +54,27 @@ func (s *ColliderSystem) Run(dt time.Duration) { position := s.Positions.Get(entity) scale := s.Scales.Get(entity) + rotation := s.Rotations.Get(entity) aabb := s.AABB.Get(entity) if aabb == nil { aabb = s.AABB.Create(entity, stdcomponents.AABB{}) } - aabb.Min = vectors.Vec2{ - X: position.X - (boxCollider.Offset.X * scale.X), - Y: position.Y - (boxCollider.Offset.Y * scale.Y), - } - aabb.Max = vectors.Vec2{ - X: position.X + (boxCollider.Width-boxCollider.Offset.X)*scale.X, - Y: position.Y + (boxCollider.Height-boxCollider.Offset.Y)*scale.Y, - } + + a := boxCollider.WH + b := vectors.Vec2{X: 0, Y: boxCollider.WH.Y} + c := vectors.Vec2{X: 0, Y: 0} + d := vectors.Vec2{X: boxCollider.WH.X, Y: 0} + + c = c.Sub(boxCollider.Offset).Rotate(rotation.Angle) + a = a.Sub(boxCollider.Offset).Rotate(rotation.Angle) + b = b.Sub(boxCollider.Offset).Rotate(rotation.Angle) + d = d.Sub(boxCollider.Offset).Rotate(rotation.Angle) + + aabb.Min = vectors.Vec2{X: min(b.X, c.X, a.X, d.X), Y: min(b.Y, c.Y, a.Y, d.Y)}.Mul(scale.XY) + aabb.Max = vectors.Vec2{X: max(b.X, c.X, a.X, d.X), Y: max(b.Y, c.Y, a.Y, d.Y)}.Mul(scale.XY) + + aabb.Min = position.XY.Add(aabb.Min) + aabb.Max = position.XY.Add(aabb.Max) return true }) @@ -91,14 +102,8 @@ func (s *ColliderSystem) Run(dt time.Duration) { aabb = s.AABB.Create(entity, stdcomponents.AABB{}) } - aabb.Min = vectors.Vec2{ - X: position.X - (circleCollider.Offset.X * scale.X), - Y: position.Y + (circleCollider.Radius-circleCollider.Offset.Y)*scale.Y, - } - aabb.Max = vectors.Vec2{ - X: position.X + (circleCollider.Radius-circleCollider.Offset.X)*scale.X, - Y: position.Y - (circleCollider.Offset.Y * scale.Y), - } + aabb.Min = position.XY.Sub(circleCollider.Offset.Mul(scale.XY)) + aabb.Max = position.XY.Add(circleCollider.Offset.Scale(-1).SubScalar(circleCollider.Radius).Mul(scale.XY)) return true }) diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 6d159241..c3dded56 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -28,7 +28,6 @@ import ( const ( EPA_TOLERANCE = 0.00001 EPA_MAX_ITERATIONS = 64 - MIN_NORMAL_LENGTH = 0.00001 ) func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { @@ -117,7 +116,10 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { Depth: event.depth, }) - s.Positions.Create(proxy, stdcomponents.Position{X: pos.X, Y: pos.Y}) + s.Positions.Create(proxy, stdcomponents.Position{ + XY: vectors.Vec2{ + X: pos.X, Y: pos.Y, + }}) s.activeCollisions[pair] = proxy } else { proxy := s.activeCollisions[pair] @@ -126,8 +128,8 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { collision.State = stdcomponents.CollisionStateStay collision.Depth = event.depth collision.Normal = event.normal - position.X = pos.X - position.Y = pos.Y + position.XY.X = pos.X + position.XY.Y = pos.Y } } close(doneChan) @@ -222,7 +224,7 @@ func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB std // If collision detected, get penetration details using EPA normal, depth := s.epa(simplex, supportA, supportB) - position := posA.Add(posB.Sub(*posA)) + position := posA.XY.Add(posB.XY.Sub(posA.XY)) return CollisionEvent{ entityA: entityA, entityB: entityB, @@ -232,50 +234,10 @@ func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB std }, true } -func (s *CollisionDetectionBVHSystem) checkCollision(colliderA, colliderB stdcomponents.GenericCollider, entityA, entityB ecs.Entity) bool { - posA := s.Positions.Get(entityA) - posB := s.Positions.Get(entityB) - scaleA := s.getScaleOrDefault(entityA) - scaleB := s.getScaleOrDefault(entityB) - - switch colliderA.Shape { - case stdcomponents.BoxColliderShape: - a := s.BoxColliders.Get(entityA) - switch colliderB.Shape { - case stdcomponents.BoxColliderShape: - return true // AABB overlap already confirmed - case stdcomponents.CircleColliderShape: - b := s.CircleColliders.Get(entityB) - return s.circleVsBox(b, *posB, scaleB, a, *posA, scaleA) - default: - return false - } - case stdcomponents.CircleColliderShape: - a := s.CircleColliders.Get(entityA) - switch colliderB.Shape { - case stdcomponents.BoxColliderShape: - b := s.BoxColliders.Get(entityB) - return s.circleVsBox(a, *posA, scaleA, b, *posB, scaleB) - case stdcomponents.CircleColliderShape: - b := s.CircleColliders.Get(entityB) - dx := posA.X - posB.X - dy := posA.Y - posB.Y - distanceSq := dx*dx + dy*dy - radiusA := a.Radius * scaleA.X - radiusB := b.Radius * scaleB.X - return distanceSq <= (radiusA+radiusB)*(radiusA+radiusB) - default: - return false - } - default: - return false - } -} - func (s *CollisionDetectionBVHSystem) getScaleOrDefault(entity ecs.Entity) vectors.Vec2 { scale := s.Scales.Get(entity) if scale != nil { - return vectors.Vec2{X: scale.X, Y: scale.Y} // Dereference the component pointer + return scale.XY // Dereference the component pointer } // Return default scale of 1 if component doesn't exist return vectors.Vec2{X: 1, Y: 1} @@ -290,29 +252,6 @@ func (s *CollisionDetectionBVHSystem) getRotationOrDefault(entity ecs.Entity) st return stdcomponents.Rotation{Angle: 0} } -func (s *CollisionDetectionBVHSystem) circleVsBox(circleCollider *stdcomponents.CircleCollider, circlePos stdcomponents.Position, circleScale vectors.Vec2, boxCollider *stdcomponents.BoxCollider, boxPos stdcomponents.Position, boxScale vectors.Vec2) bool { - radius := circleCollider.Radius * circleScale.X - boxWidth := boxCollider.Width * boxScale.X - boxHeight := boxCollider.Height * boxScale.Y - - halfWidth := boxWidth / 2 - halfHeight := boxHeight / 2 - - boxMinX := boxPos.X - halfWidth - boxMaxX := boxPos.X + halfWidth - boxMinY := boxPos.Y - halfHeight - boxMaxY := boxPos.Y + halfHeight - - closestX := max(boxMinX, min(circlePos.X, boxMaxX)) - closestY := max(boxMinY, min(circlePos.Y, boxMaxY)) - - dx := circlePos.X - closestX - dy := circlePos.Y - closestY - distanceSq := dx*dx + dy*dy - - return distanceSq <= radius*radius -} - func (s *CollisionDetectionBVHSystem) processExitStates() { for pair, proxy := range s.activeCollisions { if _, exists := s.currentCollisions[pair]; !exists { @@ -356,34 +295,29 @@ func (s *CollisionDetectionBVHSystem) getSupportFunction(entity ecs.Entity, coll func (s *CollisionDetectionBVHSystem) circleSupport(circle *stdcomponents.CircleCollider, pos *stdcomponents.Position, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { if direction.LengthSquared() == 0 { - return vectors.Vec2{X: pos.X, Y: pos.Y} + return pos.XY } radius := circle.Radius * scale.X dirNorm := direction.Normalize() - return vectors.Vec2{ - X: pos.X + dirNorm.X*radius, - Y: pos.Y + dirNorm.Y*radius, - } + return pos.XY.Add(dirNorm.Scale(radius)) } func (s *CollisionDetectionBVHSystem) boxSupport(box *stdcomponents.BoxCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { - halfWidth := box.Width * scale.X / 2 - halfHeight := box.Height * scale.Y / 2 + scaledWH := box.WH.Mul(scale) vertices := [4]vectors.Vec2{ - {X: halfWidth, Y: halfHeight}, - {X: -halfWidth, Y: halfHeight}, - {X: -halfWidth, Y: -halfHeight}, - {X: halfWidth, Y: -halfHeight}, + {X: scaledWH.X, Y: scaledWH.Y}, + {X: 0, Y: scaledWH.Y}, + {X: 0, Y: 0}, + {X: scaledWH.X, Y: 0}, } var maxPoint vectors.Vec2 var maxDistance float32 = -math.MaxFloat32 for i := range vertices { - // Apply rotation - rotated := vertices[i].Rotate(rot.Angle) - // Move to world space - worldVertex := vectors.Vec2{X: pos.X + rotated.X, Y: pos.Y + rotated.Y} + vertex := &vertices[i] + rotated := vertex.Sub(box.Offset.Mul(scale)).Rotate(rot.Angle) + worldVertex := pos.XY.Add(rotated) distance := worldVertex.Dot(direction) if distance > maxDistance { @@ -402,7 +336,7 @@ func (s *CollisionDetectionBVHSystem) polygonSupport(poly *stdcomponents.Polygon for _, v := range poly.Vertices { scaled := vectors.Vec2{X: v.X * scale.X, Y: v.Y * scale.Y} rotated := scaled.Rotate(rot.Angle) - worldVertex := vectors.Vec2{X: pos.X + rotated.X, Y: pos.Y + rotated.Y} + worldVertex := pos.XY.Add(rotated) dot := float64(worldVertex.Dot(direction)) if dot > maxDot { maxDot = dot @@ -580,7 +514,7 @@ func (s *CollisionDetectionBVHSystem) epa(simplex []vectors.Vec2, supportA, supp } } - return minNormal, minDistance + EPA_TOLERANCE + return minNormal, minDistance } func (s *CollisionDetectionBVHSystem) findClosestEdge(polytope []vectors.Vec2) (СlosestEdge, vectors.Vec2) { diff --git a/stdsystems/collision-detection-grid.go b/stdsystems/collision-detection-grid.go index b35c09fe..8cfcf733 100644 --- a/stdsystems/collision-detection-grid.go +++ b/stdsystems/collision-detection-grid.go @@ -94,8 +94,8 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { scale := s.Scales.Get(entity) collider := s.GenericCollider.Get(entity) - cellX := int(position.X-(collider.Offset.X*scale.X)) / s.cellSizeX - cellY := int(position.Y-(collider.Offset.Y*scale.Y)) / s.cellSizeY + cellX := int(position.XY.X-(collider.Offset.X*scale.XY.X)) / s.cellSizeX + cellY := int(position.XY.Y-(collider.Offset.Y*scale.XY.Y)) / s.cellSizeY cell := stdcomponents.SpatialIndex{X: cellX, Y: cellY} s.entityToCell[entity] = cell s.spatialBuckets[cell] = append(s.spatialBuckets[cell], entity) @@ -109,10 +109,10 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { collider := s.BoxColliders.Get(entity) scale := s.Scales.Get(entity) newAABB := aabb{ - Left: position.X - (collider.Offset.X * scale.X), - Right: position.X + (collider.Width-collider.Offset.X)*scale.X, - Top: position.Y - (collider.Offset.Y * scale.Y), - Bottom: position.Y + (collider.Height-collider.Offset.Y)*scale.Y, + Left: position.XY.X - (collider.Offset.X * scale.XY.X), + Right: position.XY.X + (collider.WH.X-collider.Offset.X)*scale.XY.X, + Top: position.XY.Y - (collider.Offset.Y * scale.XY.Y), + Bottom: position.XY.Y + (collider.WH.Y-collider.Offset.Y)*scale.XY.Y, } s.aabbs[entity] = newAABB return true @@ -131,13 +131,18 @@ func (s *CollisionDetectionGridSystem) Run(dt time.Duration) { if _, exists := s.activeCollisions[pair]; !exists { proxy := s.EntityManager.Create() s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) - s.Positions.Create(proxy, stdcomponents.Position{X: event.position.X, Y: event.position.Y}) + s.Positions.Create(proxy, stdcomponents.Position{ + XY: vectors.Vec2{ + X: event.position.X, + Y: event.position.Y, + }, + }) s.activeCollisions[pair] = proxy } else { proxy := s.activeCollisions[pair] s.Collisions.Get(proxy).State = stdcomponents.CollisionStateStay - s.Positions.Get(proxy).X = event.position.X - s.Positions.Get(proxy).Y = event.position.Y + s.Positions.Get(proxy).XY.X = event.position.X + s.Positions.Get(proxy).XY.Y = event.position.Y } } close(doneChan) @@ -212,12 +217,12 @@ func (s *CollisionDetectionGridSystem) registerCollision(entityA, entityB ecs.En if _, exists := s.activeCollisions[pair]; !exists { proxy := s.EntityManager.Create() s.Collisions.Create(proxy, stdcomponents.Collision{E1: pair.E1, E2: pair.E2, State: stdcomponents.CollisionStateEnter}) - s.Positions.Create(proxy, stdcomponents.Position{X: posX, Y: posY}) + s.Positions.Create(proxy, stdcomponents.Position{XY: vectors.Vec2{X: posX, Y: posY}}) s.activeCollisions[pair] = proxy } else { s.Collisions.Get(s.activeCollisions[pair]).State = stdcomponents.CollisionStateStay - s.Positions.Get(s.activeCollisions[pair]).X = posX - s.Positions.Get(s.activeCollisions[pair]).Y = posY + s.Positions.Get(s.activeCollisions[pair]).XY.X = posX + s.Positions.Get(s.activeCollisions[pair]).XY.Y = posY } } @@ -284,8 +289,8 @@ func (s *CollisionDetectionGridSystem) boxToXCollision(entityA ecs.Entity, colli continue } - posX := (position1.X + position2.X) / 2 - posY := (position1.Y + position2.Y) / 2 + posX := (position1.XY.X + position2.XY.X) / 2 + posY := (position1.XY.Y + position2.XY.Y) / 2 collisionChan <- CollisionEvent{entityA, entityB, vectors.Vec2{posX, posY}, vectors.Vec2{posX, posY}, 0} case stdcomponents.CircleColliderShape: panic("Circle-Box collision not implemented") diff --git a/stdsystems/collision-reslution.go b/stdsystems/collision-reslution.go index f43aa8b3..f1c22b7c 100644 --- a/stdsystems/collision-reslution.go +++ b/stdsystems/collision-reslution.go @@ -17,6 +17,7 @@ package stdsystems import ( "gomp/pkg/ecs" "gomp/stdcomponents" + "gomp/vectors" "time" ) @@ -36,7 +37,7 @@ func (s *CollisionResolutionSystem) Run(dt time.Duration) { s.Collisions.EachComponent(func(collision *stdcomponents.Collision) bool { if collision.State == stdcomponents.CollisionStateEnter || collision.State == stdcomponents.CollisionStateStay { // Resolve penetration - displacement := collision.Normal.Scale(collision.Depth * 0.5) + var displacement vectors.Vec2 // Move entities apart rigidbody1 := s.RigidBodies.Get(collision.E1) @@ -46,16 +47,24 @@ func (s *CollisionResolutionSystem) Run(dt time.Duration) { return true } + if !rigidbody1.IsStatic && !rigidbody2.IsStatic { + // both objects are dynamic + displacement = collision.Normal.Scale(collision.Depth * 0.5) + } else { + // one of the objects is static + displacement = collision.Normal.Scale(collision.Depth) + } + if !rigidbody1.IsStatic { p1 := s.Positions.Get(collision.E1) - p1d := p1.Sub(displacement) - p1.X, p1.Y = p1d.X, p1d.Y + p1d := p1.XY.Sub(displacement) + p1.XY.X, p1.XY.Y = p1d.X, p1d.Y } if !rigidbody2.IsStatic { p2 := s.Positions.Get(collision.E2) - p2d := p2.Add(displacement) - p2.X, p2.Y = p2d.X, p2d.Y + p2d := p2.XY.Add(displacement) + p2.XY.X, p2.XY.Y = p2d.X, p2d.Y } // Apply forces or velocity changes diff --git a/stdsystems/network-send.go b/stdsystems/network-send.go index d32f6001..66b7635b 100644 --- a/stdsystems/network-send.go +++ b/stdsystems/network-send.go @@ -41,7 +41,7 @@ func (s *NetworkSendSystem) Init() { s.Positions.SetEncoder(func(components []stdcomponents.Position) []byte { data := make([]byte, 0) for _, component := range components { - binary := fmt.Sprintf("%b", component.X) + binary := fmt.Sprintf("%b", component.XY.X) data = append(data, []byte(binary)...) } diff --git a/stdsystems/sprite-matrix.go b/stdsystems/sprite-matrix.go index 056bc59a..68ce5913 100644 --- a/stdsystems/sprite-matrix.go +++ b/stdsystems/sprite-matrix.go @@ -27,9 +27,9 @@ type SpriteMatrixSystem struct { func (s *SpriteMatrixSystem) Init() {} func (s *SpriteMatrixSystem) Run() { s.SpriteMatrixes.EachEntityParallel(func(entity ecs.Entity) bool { - spriteMatrix := s.SpriteMatrixes.Get(entity) // - animationState := s.AnimationStates.Get(entity) // + spriteMatrix := s.SpriteMatrixes.Get(entity) // position := s.Positions.Get(entity) + animationState := s.AnimationStates.Get(entity) // frame := spriteMatrix.Animations[*animationState].Frame @@ -39,7 +39,7 @@ func (s *SpriteMatrixSystem) Run() { Texture: spriteMatrix.Texture, // Frame: frame, // Origin: spriteMatrix.Origin, - Dest: rl.Rectangle{X: position.X, Y: position.Y, Width: frame.Width, Height: frame.Height}, // + Dest: rl.Rectangle{X: position.XY.X, Y: position.XY.Y, Width: frame.Width, Height: frame.Height}, // }) } else { // Run spriteRender diff --git a/stdsystems/sprite.go b/stdsystems/sprite.go index 99d12e51..5f676d23 100644 --- a/stdsystems/sprite.go +++ b/stdsystems/sprite.go @@ -43,12 +43,12 @@ func (s *SpriteSystem) Run() { renderable := s.Renderables.Get(entity) if renderable == nil { - s.Renderables.Create(entity, stdcomponents.SpriteRenderableType) + renderable = s.Renderables.Create(entity, stdcomponents.SpriteRenderableType) } renderOrder := s.RenderOrder.Get(entity) if renderOrder == nil { - s.RenderOrder.Create(entity, stdcomponents.RenderOrder{}) + renderOrder = s.RenderOrder.Create(entity, stdcomponents.RenderOrder{}) } tr := s.RLTexturePros.Get(entity) @@ -57,10 +57,10 @@ func (s *SpriteSystem) Run() { Texture: sprite.Texture, // Frame: sprite.Frame, // Origin: rl.Vector2{ - X: sprite.Origin.X * scale.X, - Y: sprite.Origin.Y * scale.Y, + X: sprite.Origin.X * scale.XY.X, + Y: sprite.Origin.Y * scale.XY.Y, }, - Dest: rl.Rectangle{X: position.X, Y: position.Y, Width: sprite.Frame.Width, Height: sprite.Frame.Height}, // + Dest: rl.Rectangle{X: position.XY.X, Y: position.XY.Y, Width: sprite.Frame.Width, Height: sprite.Frame.Height}, // Tint: stdcomponents.Tint{ R: 255, G: 255, diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 025efac8..903d6ddd 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -28,8 +28,8 @@ func (s *VelocitySystem) Run(dt time.Duration) { velocity := s.Velocities.Get(e) position := s.Positions.Get(e) - position.X += velocity.X * dtSec - position.Y += velocity.Y * dtSec + position.XY.X += velocity.X * dtSec + position.XY.Y += velocity.Y * dtSec return true }) } diff --git a/stdsystems/ysort.go b/stdsystems/ysort.go index 2876b421..c505a301 100644 --- a/stdsystems/ysort.go +++ b/stdsystems/ysort.go @@ -39,7 +39,7 @@ func (s *YSortSystem) Run() { renderOrder := s.RenderOrders.Get(entity) // Calculate depth based on Y position - yDepth := pos.Y * ySortOffsetScale + yDepth := pos.XY.Y * ySortOffsetScale // Preserve original Z layer but add Y-based offset //renderOrder.CalculatedZ = float32(int(pos.Z)) + yDepth diff --git a/vectors/radian.go b/vectors/radian.go new file mode 100644 index 00000000..b00567da --- /dev/null +++ b/vectors/radian.go @@ -0,0 +1,17 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package vectors + +type Radians = float64 diff --git a/vectors/vector2.go b/vectors/vector2.go index af14f000..7cb254be 100644 --- a/vectors/vector2.go +++ b/vectors/vector2.go @@ -14,7 +14,9 @@ Thank you for your support! package vectors -import "math" +import ( + "math" +) type Vec2 struct { X, Y float32 @@ -56,10 +58,10 @@ func (v Vec2) Normalize() Vec2 { return v.Scale(1 / v.Length()) } -func (v Vec2) Rotate(angle float32) Vec2 { +func (v Vec2) Rotate(angle Radians) Vec2 { return Vec2{ - v.X*float32(math.Cos(float64(angle))) - v.Y*float32(math.Sin(float64(angle))), - v.X*float32(math.Sin(float64(angle))) + v.Y*float32(math.Cos(float64(angle))), + v.X*float32(math.Cos(angle)) - v.Y*float32(math.Sin(angle)), + v.X*float32(math.Sin(angle)) + v.Y*float32(math.Cos(angle)), } } @@ -82,16 +84,3 @@ func (v Vec2) Perpendicular() Vec2 { func (v Vec2) Dot(other Vec2) float32 { return v.X*other.X + v.Y*other.Y } - -const ( - MIN_NORMAL_LENGTH = 0.00001 -) - -// NormalizedSafe is safe normalization method -func (v Vec2) NormalizedSafe() Vec2 { - lengthSq := v.X*v.X + v.Y*v.Y - if lengthSq < MIN_NORMAL_LENGTH { - return Vec2{1, 0} // Default to right direction - } - return v.Scale(float32(1 / math.Sqrt(float64(lengthSq)))) -} From 614dbd87bf37e250c11afad4e8df053557b10cb7 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 23 Mar 2025 22:40:50 +0300 Subject: [PATCH 057/196] collision-detection-bvh.go small fixes --- examples/new-api/systems/asterodd.go | 2 +- stdsystems/collision-detection-bvh.go | 54 ++++----------------------- 2 files changed, 8 insertions(+), 48 deletions(-) diff --git a/examples/new-api/systems/asterodd.go b/examples/new-api/systems/asterodd.go index 5d2c8616..ffb7beed 100644 --- a/examples/new-api/systems/asterodd.go +++ b/examples/new-api/systems/asterodd.go @@ -63,7 +63,7 @@ func (s *AssteroddSystem) Init() { Hps: s.Hps, Weapons: s.Weapons, SpaceshipIntents: s.SpaceshipIntents, - }, 300, 300, 0) + }, 300, 300, -44.9) entities.CreateSatellite(entities.CreateSatelliteManagers{ EntityManager: s.EntityManager, Positions: s.Positions, diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index c3dded56..0c532f4e 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -303,12 +303,11 @@ func (s *CollisionDetectionBVHSystem) circleSupport(circle *stdcomponents.Circle } func (s *CollisionDetectionBVHSystem) boxSupport(box *stdcomponents.BoxCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { - scaledWH := box.WH.Mul(scale) vertices := [4]vectors.Vec2{ - {X: scaledWH.X, Y: scaledWH.Y}, - {X: 0, Y: scaledWH.Y}, + {X: box.WH.X, Y: box.WH.Y}, + {X: 0, Y: box.WH.Y}, {X: 0, Y: 0}, - {X: scaledWH.X, Y: 0}, + {X: box.WH.X, Y: 0}, } var maxPoint vectors.Vec2 @@ -316,8 +315,7 @@ func (s *CollisionDetectionBVHSystem) boxSupport(box *stdcomponents.BoxCollider, for i := range vertices { vertex := &vertices[i] - rotated := vertex.Sub(box.Offset.Mul(scale)).Rotate(rot.Angle) - worldVertex := pos.XY.Add(rotated) + worldVertex := vertex.Sub(box.Offset).Rotate(rot.Angle).Mul(scale).Add(pos.XY) distance := worldVertex.Dot(direction) if distance > maxDistance { @@ -436,47 +434,6 @@ func (s *CollisionDetectionBVHSystem) tripleProduct(a, b, c vectors.Vec2) vector } } -/* - function EPA(polytope, shapeA, shapeB) { - let minIndex = 0; - let minDistance = Infinity; - let minNormal; - - while (minDistance == Infinity) { - for (let i = 0; i < polytope.length; i++) { - let j = (i + 1) % polytope.length; - - let vertexI = polytope[i].copy(); - let vertexJ = polytope[j].copy(); - - let ij = vertexJ.sub(vertexI); - - let normal = createVector(ij.y, -ij.x).normalize(); - let distance = normal.dot(vertexI); - - if (distance < 0) { - distance *= -1; - normal.mult(-1); - } - - if (distance < minDistance) { - minDistance = distance; - minNormal = normal; - minIndex = j; - } - } - let support = support(shapeA, shapeB, minNormal); - let sDistance = minNormal.dot(support); - - if (abs(sDistance - minDistance) > 0.001) { - minDistance = Infinity; - polytope.splice(minIndex, 0, support); - } - } - - return minNormal.mult(minDistance + 0.001); - } -*/ func (s *CollisionDetectionBVHSystem) epa(simplex []vectors.Vec2, supportA, supportB func(vectors.Vec2) vectors.Vec2) (vectors.Vec2, float32) { var minIndex int = 0 var minDistance float32 = float32(math.MaxFloat32) @@ -489,6 +446,9 @@ func (s *CollisionDetectionBVHSystem) epa(simplex []vectors.Vec2, supportA, supp b := simplex[j] edge := b.Sub(a) + if edge.X == 0 && edge.Y == 0 { + panic("jk") + } normal := vectors.Vec2{edge.Y, -edge.X}.Normalize() distance := normal.Dot(a) From f3564a6bff24fcc3d95fbe877583f716bba78b85 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 23 Mar 2025 23:01:00 +0300 Subject: [PATCH 058/196] fix naming collisions --- aos_soa_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aos_soa_test.go b/aos_soa_test.go index 756e9568..d23a9a87 100644 --- a/aos_soa_test.go +++ b/aos_soa_test.go @@ -125,7 +125,7 @@ func OffsetSystem(offsets [][pageSize][AdditionalDataPer64]int64) { } } -func RenderSystem(positions [][pageSize]Float3, unitsToRender []int) { +func renderSystem(positions [][pageSize]Float3, unitsToRender []int) { for i := 0; i < len(positions); i++ { for j := 0; j < len(positions[i]); j++ { if ShouldRender(&positions[i][j]) { @@ -170,7 +170,7 @@ func BenchmarkUpdateRodd(b *testing.B) { PositionSystem(positions, velocities) TakeDamageSystem(hps) OffsetSystem(offsets) - RenderSystem(positions, unitsToRender) + renderSystem(positions, unitsToRender) unitsToRender = unitsToRender[:0] } } From 46ff981f14528e5a06f54243b7610b8ed72077fb Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 23 Mar 2025 23:05:30 +0300 Subject: [PATCH 059/196] fix taskfile dev cmd --- taskfile.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/taskfile.yml b/taskfile.yml index 3dc7ce2f..f5916a04 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -3,7 +3,7 @@ version: "3" tasks: dev: cmds: - - air + - go run ./examples/new-api/game.go server: cmds: @@ -47,8 +47,8 @@ tasks: CGO_ENABLED: 1 cmds: - go build -o ./.dist/game-win64.exe -tags opengl43 examples/new-api/game.go -# - go build -o ./.dist/cgo-game.exe internal/sdl3-pure/sdl-game.go -# - CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -tags static -ldflags "-s -w" internal/sdl2/sdl-game.go + # - go build -o ./.dist/cgo-game.exe internal/sdl3-pure/sdl-game.go + # - CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 go build -tags static -ldflags "-s -w" internal/sdl2/sdl-game.go build-mac: - task: build-darwin-amd64 From 3f5683ef28d5acc64b1a98a988fbdadbc542c935 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 24 Mar 2025 03:45:58 +0300 Subject: [PATCH 060/196] feat gjk package amd refactor collision detection system --- pkg/collision/gjk.go | 108 +++++++++++++ pkg/collision/simplex.go | 119 ++++++++++++++ stdcomponents/colliders.go | 26 +++ stdcomponents/ids.go | 1 + stdcomponents/transform.go | 32 ++++ stdsystems/collision-detection-bvh.go | 218 +++----------------------- vectors/vector2.go | 11 ++ vectors/vector3.go | 99 ++++++++++++ 8 files changed, 421 insertions(+), 193 deletions(-) create mode 100644 pkg/collision/gjk.go create mode 100644 pkg/collision/simplex.go create mode 100644 stdcomponents/transform.go create mode 100644 vectors/vector3.go diff --git a/pkg/collision/gjk.go b/pkg/collision/gjk.go new file mode 100644 index 00000000..6f8fe942 --- /dev/null +++ b/pkg/collision/gjk.go @@ -0,0 +1,108 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +Thank you for your support! +*/ + +package gjk + +import ( + "gomp/stdcomponents" + "gomp/vectors" + "math" +) + +const ( + maxItterations = 64 + epaTolerance = 0.00001 +) + +type AnyCollider interface { + GetSupport(direction vectors.Vec2, transform *stdcomponents.Transform2d) vectors.Vec2 +} + +func CheckCollision( + a, b AnyCollider, + transformA, transformB *stdcomponents.Transform2d, +) (Simplex2d, bool) { + direction := vectors.Vec2{X: 1, Y: 0} + simplex := Simplex2d{} + + p := minkowskiSupport2d(a, b, transformA, transformB, direction) + simplex.add(p.ToVec3()) + direction = p.Neg() + + for range maxItterations { + p = minkowskiSupport2d(a, b, transformA, transformB, direction) + + if p.Dot(direction) < 0 { + return simplex, false + } + + simplex.add(p.ToVec3()) + + if simplex.do(&direction) { + return simplex, true + } + } + + panic("Infinite loop") +} + +func EPA( + a, b AnyCollider, + transformA, transformB *stdcomponents.Transform2d, + simplex *Simplex2d, +) (vectors.Vec2, float32) { + var minIndex int = 0 + var minDistance float32 = float32(math.MaxFloat32) + var minNormal vectors.Vec2 + var polytope = simplex.toPolytope(make([]vectors.Vec2, 0, 6)) + + for minDistance == float32(math.MaxFloat32) { + for i := 0; i < len(polytope); i++ { + j := (i + 1) % len(polytope) + a := polytope[i] + b := polytope[j] + + edge := b.Sub(a) + if edge.X == 0 && edge.Y == 0 { + panic("jk") + } + + normal := edge.Normal().Normalize() + distance := normal.Dot(a) + + if distance < 0 { + distance *= -1 + normal = normal.Neg() + } + + if distance < minDistance { + minDistance = distance + minNormal = normal + minIndex = j + } + } + + support := minkowskiSupport2d(a, b, transformA, transformB, minNormal) + sDistance := minNormal.Dot(support) + + if math.Abs(float64(sDistance-minDistance)) > epaTolerance { + minDistance = float32(math.MaxFloat32) + polytope = append(polytope[:minIndex], append([]vectors.Vec2{support}, polytope[minIndex:]...)...) + } + } + + return minNormal, minDistance + epaTolerance +} + +func minkowskiSupport2d(a, b AnyCollider, transformA, transformB *stdcomponents.Transform2d, direction vectors.Vec2) vectors.Vec2 { + return a.GetSupport(direction, transformA).Sub(b.GetSupport(direction.Neg(), transformB)) +} diff --git a/pkg/collision/simplex.go b/pkg/collision/simplex.go new file mode 100644 index 00000000..013d1221 --- /dev/null +++ b/pkg/collision/simplex.go @@ -0,0 +1,119 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +Thank you for your support! +*/ + +package gjk + +import "gomp/vectors" + +type Simplex2d struct { + a, b, c vectors.Vec3 + count int +} + +func (s *Simplex2d) do(direction *vectors.Vec2) bool { + ao := s.a.Neg() + + switch s.count { + case 2: // Line + ab := s.b.Sub(s.a) + if ab.Dot(ao) > 0 { + newDirection := ab.Cross(ao).Cross(ab) + direction.X = newDirection.X + direction.Y = newDirection.Y + } else { + direction.X = ao.X + direction.Y = ao.Y + s.count = 1 + } + case 3: // Triangle + ab := s.b.Sub(s.a) + ac := s.c.Sub(s.a) + abc := ab.Cross(ac) + + if abc.Cross(ac).Dot(ao) > 0 { + if ac.Dot(ao) > 0 { + newDirection := ac.Cross(ao).Cross(ac) + direction.X = newDirection.X + direction.Y = newDirection.Y + s.b = s.c + s.count = 2 + } else { + if ab.Dot(ao) > 0 { + newDirection := ab.Cross(ao).Cross(ab) + direction.X = newDirection.X + direction.Y = newDirection.Y + s.count = 2 + } else { + direction.X = ao.X + direction.Y = ao.Y + s.count = 1 + } + } + } else { + if ab.Cross(abc).Dot(ao) > 0 { + if ab.Dot(ao) > 0 { + newDirection := ab.Cross(ao).Cross(ab) + direction.X = newDirection.X + direction.Y = newDirection.Y + s.count = 2 + } else { + direction.X = ao.X + direction.Y = ao.Y + s.count = 1 + } + } else { + return true + // if abc.Dot(ao) > 0 { + // newDirection := abc + // direction.X = newDirection.X + // direction.Y = newDirection.Y + // } else { + // s.b, s.c = s.a, s.b + // newDirection := abc.Neg() + // direction.X = newDirection.X + // direction.Y = newDirection.Y + // } + } + } + default: + panic("Invalid simplex") + } + return false +} + +func (s *Simplex2d) add(p vectors.Vec3) { + switch s.count { + case 0: + s.a = p + case 1: + s.a, s.b = p, s.a + case 2: + s.a, s.b, s.c = p, s.a, s.b + default: + panic("Invalid simplex") + } + s.count++ +} + +func (s *Simplex2d) toPolytope(polytope []vectors.Vec2) []vectors.Vec2 { + switch s.count { + case 1: + polytope = append(polytope, s.a.ToVec2()) + case 2: + polytope = append(polytope, s.a.ToVec2(), s.b.ToVec2()) + case 3: + polytope = append(polytope, s.a.ToVec2(), s.b.ToVec2(), s.c.ToVec2()) + default: + panic("Invalid simplex") + } + return polytope +} diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 2cc5d01b..43f08156 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -17,6 +17,7 @@ package stdcomponents import ( "gomp/pkg/ecs" "gomp/vectors" + "math" ) type ColliderShape uint8 @@ -47,6 +48,31 @@ type BoxCollider struct { Mask CollisionMask } +func (c *BoxCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { + var maxDistance float32 = -math.MaxFloat32 + var maxPoint vectors.Vec2 + + vertices := [4]vectors.Vec2{ + {X: c.WH.X, Y: c.WH.Y}, + {X: 0, Y: c.WH.Y}, + {X: 0, Y: 0}, + {X: c.WH.X, Y: 0}, + } + + for i := range vertices { + vertex := &vertices[i] + worldVertex := vertex.Sub(c.Offset).Rotate(transform.Rotation) + + distance := worldVertex.Dot(direction) + if distance > maxDistance { + maxDistance = distance + maxPoint = worldVertex + } + } + + return maxPoint.Mul(transform.Scale).Add(transform.Position) +} + type BoxColliderComponentManager = ecs.ComponentManager[BoxCollider] func NewBoxColliderComponentManager() BoxColliderComponentManager { diff --git a/stdcomponents/ids.go b/stdcomponents/ids.go index ea9cbbe2..063e8bd5 100644 --- a/stdcomponents/ids.go +++ b/stdcomponents/ids.go @@ -12,6 +12,7 @@ import ( const ( InvalidComponentId ecs.ComponentId = iota + TransformComponentId PositionComponentId RotationComponentId ScaleComponentId diff --git a/stdcomponents/transform.go b/stdcomponents/transform.go new file mode 100644 index 00000000..7dd3e18f --- /dev/null +++ b/stdcomponents/transform.go @@ -0,0 +1,32 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" + "gomp/vectors" +) + +type Transform2d struct { + Position vectors.Vec2 + Rotation vectors.Radians + Scale vectors.Vec2 +} + +type TransformComponentManager = ecs.ComponentManager[Transform2d] + +func NewTransformComponentManager() TransformComponentManager { + return ecs.NewComponentManager[Transform2d](TransformComponentId) +} diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 0c532f4e..551d878f 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -16,6 +16,7 @@ package stdsystems import ( "gomp/pkg/bvh" + gjk "gomp/pkg/collision" "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" @@ -196,7 +197,7 @@ func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, } colliderB := s.GenericCollider.Get(entityB) - collision, ok := s.checkCollisionGjk(*colliderA, *colliderB, entityA, entityB) + collision, ok := s.checkCollisionGjk(colliderA, colliderB, entityA, entityB) if ok { collisionChan <- collision } @@ -204,26 +205,34 @@ func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, } } -func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB stdcomponents.GenericCollider, entityA, entityB ecs.Entity) (e CollisionEvent, ok bool) { +func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB *stdcomponents.GenericCollider, entityA, entityB ecs.Entity) (e CollisionEvent, ok bool) { + colA := s.getGjkCollider(colliderA, entityA) + colB := s.getGjkCollider(colliderB, entityB) posA := s.Positions.Get(entityA) posB := s.Positions.Get(entityB) scaleA := s.getScaleOrDefault(entityA) scaleB := s.getScaleOrDefault(entityB) - rotA := s.getRotationOrDefault(entityA) // Implement similar to getScaleOrDefault + rotA := s.getRotationOrDefault(entityA) rotB := s.getRotationOrDefault(entityB) - - // Define support functions based on collider types - supportA := s.getSupportFunction(entityA, colliderA, posA, &rotA, scaleA) - supportB := s.getSupportFunction(entityB, colliderB, posB, &rotB, scaleB) + transformA := stdcomponents.Transform2d{ + Position: posA.XY, + Rotation: rotA.Angle, + Scale: scaleA, + } + transformB := stdcomponents.Transform2d{ + Position: posB.XY, + Rotation: rotB.Angle, + Scale: scaleB, + } // First detect collision using GJK - collision, simplex := s.gjkCollides(supportA, supportB) + simplex, collision := gjk.CheckCollision(colA, colB, &transformA, &transformB) if !collision { return e, false } // If collision detected, get penetration details using EPA - normal, depth := s.epa(simplex, supportA, supportB) + normal, depth := gjk.EPA(colA, colB, &transformA, &transformB, &simplex) position := posA.XY.Add(posB.XY.Sub(posA.XY)) return CollisionEvent{ entityA: entityA, @@ -271,26 +280,18 @@ func (s *CollisionDetectionBVHSystem) buildBVH(entities []ecs.Entity, aabbs []st } -func (s *CollisionDetectionBVHSystem) getSupportFunction(entity ecs.Entity, collider stdcomponents.GenericCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2) func(vectors.Vec2) vectors.Vec2 { +func (s *CollisionDetectionBVHSystem) getGjkCollider(collider *stdcomponents.GenericCollider, entity ecs.Entity) gjk.AnyCollider { switch collider.Shape { case stdcomponents.BoxColliderShape: - box := s.BoxColliders.Get(entity) - return func(d vectors.Vec2) vectors.Vec2 { - return s.boxSupport(box, pos, rot, scale, d) - } + return s.BoxColliders.Get(entity) case stdcomponents.CircleColliderShape: - circle := s.CircleColliders.Get(entity) - return func(d vectors.Vec2) vectors.Vec2 { - return s.circleSupport(circle, pos, scale, d) - } + // return s.CircleColliders.Get(entity) case stdcomponents.PolygonColliderShape: - poly := s.PolygonColliders.Get(entity) - return func(d vectors.Vec2) vectors.Vec2 { - return s.polygonSupport(poly, pos, rot, scale, d) - } + // return s.PolygonColliders.Get(entity) default: panic("unsupported collider shape") } + return nil } func (s *CollisionDetectionBVHSystem) circleSupport(circle *stdcomponents.CircleCollider, pos *stdcomponents.Position, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { @@ -315,7 +316,7 @@ func (s *CollisionDetectionBVHSystem) boxSupport(box *stdcomponents.BoxCollider, for i := range vertices { vertex := &vertices[i] - worldVertex := vertex.Sub(box.Offset).Rotate(rot.Angle).Mul(scale).Add(pos.XY) + worldVertex := vertex.Sub(box.Offset).Rotate(rot.Angle) distance := worldVertex.Dot(direction) if distance > maxDistance { @@ -324,7 +325,7 @@ func (s *CollisionDetectionBVHSystem) boxSupport(box *stdcomponents.BoxCollider, } } - return maxPoint + return maxPoint.Mul(scale).Add(pos.XY) } func (s *CollisionDetectionBVHSystem) polygonSupport(poly *stdcomponents.PolygonCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { @@ -343,172 +344,3 @@ func (s *CollisionDetectionBVHSystem) polygonSupport(poly *stdcomponents.Polygon } return maxVertex } - -func (s *CollisionDetectionBVHSystem) gjkCollides(supportA, supportB func(vectors.Vec2) vectors.Vec2) (bool, []vectors.Vec2) { - direction := vectors.Vec2{X: 1, Y: 0} // Initial direction - simplex := []vectors.Vec2{s.minkowskiSupport(supportA, supportB, direction)} - - for { // Max iterations to prevent infinite loop - p := s.minkowskiSupport(supportA, supportB, direction) - if p.X == 0 && p.Y == 0 { - return false, simplex // No collision - } - - if p.Dot(direction) < 0 { - return false, nil // No collision - } - simplex = append(simplex, p) - ok, newSimplex := s.containsOrigin(simplex, &direction) - simplex = newSimplex - if ok { - return true, simplex - } - } -} - -func (s *CollisionDetectionBVHSystem) minkowskiSupport(supportA, supportB func(vectors.Vec2) vectors.Vec2, d vectors.Vec2) vectors.Vec2 { - a := supportA(d) - b := supportB(d.Neg()) - return a.Sub(b) -} - -func (s *CollisionDetectionBVHSystem) containsOrigin(simplex []vectors.Vec2, direction *vectors.Vec2) (bool, []vectors.Vec2) { - a := (simplex)[len(simplex)-1] // Last point added - ao := a.Neg() // Vector from A to origin - - switch len(simplex) { - case 3: // Triangle case - b := (simplex)[1] - c := (simplex)[0] - - ab := b.Sub(a) - ac := c.Sub(a) - - // Perpendicular vectors - abPerp := s.tripleProduct(ac, ab, ab) - acPerp := s.tripleProduct(ab, ac, ac) - - // Region AB - if abPerp.Dot(ao) > 0 { - simplex = []vectors.Vec2{a, b} - *direction = abPerp - return false, simplex - } - - // Region AC - if acPerp.Dot(ao) > 0 { - simplex = []vectors.Vec2{a, c} - *direction = acPerp - return false, simplex - } - - // Inside triangle - return true, simplex - - case 2: // Line segment case - b := (simplex)[0] - ab := b.Sub(a) - - // Perpendicular to AB facing origin - abPerp := ab.Perpendicular() - if abPerp.Dot(ao) > 0 { - *direction = abPerp - } else { - simplex = []vectors.Vec2{a} - *direction = ao - } - return false, simplex - - default: - return false, simplex - } -} - -// Helper function for vector triple product -func (s *CollisionDetectionBVHSystem) tripleProduct(a, b, c vectors.Vec2) vectors.Vec2 { - ac := a.Dot(c) - bc := b.Dot(c) - return vectors.Vec2{ - X: b.X*ac - a.X*bc, - Y: b.Y*ac - a.Y*bc, - } -} - -func (s *CollisionDetectionBVHSystem) epa(simplex []vectors.Vec2, supportA, supportB func(vectors.Vec2) vectors.Vec2) (vectors.Vec2, float32) { - var minIndex int = 0 - var minDistance float32 = float32(math.MaxFloat32) - var minNormal vectors.Vec2 - - for minDistance == float32(math.MaxFloat32) { - for i := 0; i < len(simplex); i++ { - j := (i + 1) % len(simplex) - a := simplex[i] - b := simplex[j] - - edge := b.Sub(a) - if edge.X == 0 && edge.Y == 0 { - panic("jk") - } - - normal := vectors.Vec2{edge.Y, -edge.X}.Normalize() - distance := normal.Dot(a) - - if distance < 0 { - distance *= -1 - normal = normal.Scale(-1) - } - - if distance < minDistance { - minDistance = distance - minNormal = normal - minIndex = j - } - } - - support := s.minkowskiSupport(supportA, supportB, minNormal) - sDistance := minNormal.Dot(support) - - if math.Abs(float64(sDistance-minDistance)) > EPA_TOLERANCE { - minDistance = float32(math.MaxFloat32) - simplex = append(simplex[:minIndex], append([]vectors.Vec2{support}, simplex[minIndex:]...)...) - } - } - - return minNormal, minDistance -} - -func (s *CollisionDetectionBVHSystem) findClosestEdge(polytope []vectors.Vec2) (СlosestEdge, vectors.Vec2) { - bestEdge := СlosestEdge{index: -1, distance: float32(math.MaxFloat32)} - var bestNormal vectors.Vec2 - - for bestEdge.distance == float32(math.MaxFloat32) { - for i := 0; i < len(polytope); i++ { - j := (i + 1) % len(polytope) - a := polytope[i] - b := polytope[j] - - edge := b.Sub(a) - - normal := vectors.Vec2{edge.Y, -edge.X}.Normalize() - distance := normal.Dot(a) - - if distance < 0 { - distance *= -1 - normal = normal.Neg() - } - - if distance < bestEdge.distance { - bestEdge.distance = distance - bestEdge.index = j - bestNormal = normal - } - } - } - - return bestEdge, bestNormal -} - -type СlosestEdge struct { - index int - distance float32 -} diff --git a/vectors/vector2.go b/vectors/vector2.go index 7cb254be..dbe2541f 100644 --- a/vectors/vector2.go +++ b/vectors/vector2.go @@ -81,6 +81,17 @@ func (v Vec2) Perpendicular() Vec2 { } } +func (v Vec2) Normal() Vec2 { + return Vec2{ + X: v.Y, + Y: -v.X, + } +} + func (v Vec2) Dot(other Vec2) float32 { return v.X*other.X + v.Y*other.Y } + +func (v Vec2) ToVec3() Vec3 { + return Vec3{v.X, v.Y, 0} +} diff --git a/vectors/vector3.go b/vectors/vector3.go new file mode 100644 index 00000000..bb3d14e6 --- /dev/null +++ b/vectors/vector3.go @@ -0,0 +1,99 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package vectors + +import ( + "math" +) + +type Vec3 struct { + X, Y, Z float32 +} + +func (v Vec3) Add(other Vec3) Vec3 { + return Vec3{v.X + other.X, v.Y + other.Y, v.Z + other.Z} +} + +func (v Vec3) Sub(other Vec3) Vec3 { + return Vec3{v.X - other.X, v.Y - other.Y, v.Z - other.Z} +} + +func (v Vec3) Mul(other Vec3) Vec3 { + return Vec3{v.X * other.X, v.Y * other.Y, v.Z * other.Z} +} + +func (a Vec3) Cross(b Vec3) Vec3 { + return Vec3{ + a.Y*b.Z - b.Y*a.Z, + a.Z*b.X - b.Z*a.X, + a.X*b.Y - b.X*a.Y, + } +} + +func (v Vec3) Div(other Vec3) Vec3 { + return Vec3{v.X / other.X, v.Y / other.Y, v.Z / other.Z} +} + +func (v Vec3) AddScalar(scalar float32) Vec3 { + return Vec3{v.X + scalar, v.Y + scalar, v.Z + scalar} +} + +func (v Vec3) SubScalar(scalar float32) Vec3 { + return Vec3{v.X - scalar, v.Y - scalar, v.Z - scalar} +} + +func (v Vec3) Scale(scalar float32) Vec3 { + return Vec3{v.X * scalar, v.Y * scalar, v.Z * scalar} +} + +func (v Vec3) Length() float32 { + return float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y + v.Z*v.Z))) +} + +func (v Vec3) Normalize() Vec3 { + return v.Scale(1 / v.Length()) +} + +func (v Vec3) Rotate(angle Radians) Vec3 { + return Vec3{ + v.X*float32(math.Cos(angle)) - v.Y*float32(math.Sin(angle)), + v.X*float32(math.Sin(angle)) + v.Y*float32(math.Cos(angle)), + v.Z, + } +} + +func (v Vec3) LengthSquared() float32 { + l := v.Length() + return l * l +} + +func (v Vec3) Neg() Vec3 { + return Vec3{-v.X, -v.Y, -v.Z} +} + +func (v Vec3) Perpendicular() Vec3 { + return Vec3{ + X: -v.Y, + Y: v.X, + } +} + +func (v Vec3) Dot(other Vec3) float32 { + return v.X*other.X + v.Y*other.Y + v.Z*other.Z +} + +func (v Vec3) ToVec2() Vec2 { + return Vec2{v.X, v.Y} +} From 86730e61ae78e19dd69c51b3e4391931318fad3e Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Mar 2025 16:18:47 +0300 Subject: [PATCH 061/196] update vscode settings --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 131dd1eb..426f3dcf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ "request": "launch", "mode": "debug", "buildFlags": "-tags 'assert'", - "program": "./cmd/raylib-ecs" + "program": "./examples/new-api" } ] } \ No newline at end of file From 3f266a9dbd901561dd4c90ca73d27a08f2e62196 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Mar 2025 16:18:53 +0300 Subject: [PATCH 062/196] upd epa --- pkg/collision/gjk.go | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/pkg/collision/gjk.go b/pkg/collision/gjk.go index 6f8fe942..c5344ad3 100644 --- a/pkg/collision/gjk.go +++ b/pkg/collision/gjk.go @@ -20,7 +20,7 @@ import ( const ( maxItterations = 64 - epaTolerance = 0.00001 + epaTolerance = 0.001 ) type AnyCollider interface { @@ -52,7 +52,7 @@ func CheckCollision( } } - panic("Infinite loop") + panic("GJK infinite loop") } func EPA( @@ -65,23 +65,21 @@ func EPA( var minNormal vectors.Vec2 var polytope = simplex.toPolytope(make([]vectors.Vec2, 0, 6)) - for minDistance == float32(math.MaxFloat32) { + for range maxItterations { for i := 0; i < len(polytope); i++ { j := (i + 1) % len(polytope) a := polytope[i] b := polytope[j] edge := b.Sub(a) - if edge.X == 0 && edge.Y == 0 { - panic("jk") - } + // normal := edge.Cross(a.ToVec3()).Cross(edge).ToVec2().Normalize() normal := edge.Normal().Normalize() - distance := normal.Dot(a) + distance := a.Dot(normal) if distance < 0 { - distance *= -1 normal = normal.Neg() + distance = -distance } if distance < minDistance { @@ -91,16 +89,24 @@ func EPA( } } - support := minkowskiSupport2d(a, b, transformA, transformB, minNormal) - sDistance := minNormal.Dot(support) + if minDistance == 0 { + return minNormal, minDistance + } + + minNormal = minNormal.Normalize() + + p := minkowskiSupport2d(a, b, transformA, transformB, minNormal) + normalDot := minNormal.Dot(p) + accuracy := math.Abs(float64(normalDot - minDistance)) - if math.Abs(float64(sDistance-minDistance)) > epaTolerance { - minDistance = float32(math.MaxFloat32) - polytope = append(polytope[:minIndex], append([]vectors.Vec2{support}, polytope[minIndex:]...)...) + if accuracy < epaTolerance { + return minNormal, minDistance } + + polytope = append(polytope[:minIndex], append([]vectors.Vec2{p}, polytope[minIndex:]...)...) } - return minNormal, minDistance + epaTolerance + panic("EPA infinite loop") } func minkowskiSupport2d(a, b AnyCollider, transformA, transformB *stdcomponents.Transform2d, direction vectors.Vec2) vectors.Vec2 { From 1bfcac6e98e9ac861eeb5929abd58325dc0f6c3a Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Mar 2025 17:05:27 +0300 Subject: [PATCH 063/196] refactor epa --- pkg/collision/epa.go | 76 ++++++++++++++++++++++++++++++++++++++++++++ pkg/collision/gjk.go | 55 -------------------------------- 2 files changed, 76 insertions(+), 55 deletions(-) create mode 100644 pkg/collision/epa.go diff --git a/pkg/collision/epa.go b/pkg/collision/epa.go new file mode 100644 index 00000000..7862f1f1 --- /dev/null +++ b/pkg/collision/epa.go @@ -0,0 +1,76 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gjk + +import ( + "gomp/stdcomponents" + "gomp/vectors" + "math" +) + +func EPA( + a, b AnyCollider, + transformA, transformB *stdcomponents.Transform2d, + simplex *Simplex2d, +) (vectors.Vec2, float32) { + polytope := simplex.toPolytope(make([]vectors.Vec2, 0, 6)) + + for range maxItterations { + edge := findClosestEdge(polytope) + point := minkowskiSupport2d(a, b, transformA, transformB, edge.normal) + distance := point.Dot(edge.normal) + if distance-edge.distance < epaTolerance { + return edge.normal, distance + } + + polytope = append(polytope[:edge.index], append([]vectors.Vec2{point}, polytope[edge.index:]...)...) + } + + panic("EPA infinite loop") +} + +func findClosestEdge(polytope []vectors.Vec2) closestEdge { + closest := closestEdge{ + distance: float32(math.MaxFloat32), + normal: vectors.Vec2{}, + index: -1, + } + + for i := 0; i < len(polytope); i++ { + j := (i + 1) % len(polytope) + a := polytope[i] + b := polytope[j] + + edge := b.Sub(a).ToVec3() + oa := a.ToVec3() + + normal := edge.Cross(oa).Cross(edge).ToVec2().Normalize() + distance := normal.Dot(a) + + if distance < closest.distance { + closest.distance = distance + closest.normal = normal + closest.index = j + } + } + + return closest +} + +type closestEdge struct { + distance float32 + normal vectors.Vec2 + index int +} diff --git a/pkg/collision/gjk.go b/pkg/collision/gjk.go index c5344ad3..fc290a4e 100644 --- a/pkg/collision/gjk.go +++ b/pkg/collision/gjk.go @@ -15,7 +15,6 @@ package gjk import ( "gomp/stdcomponents" "gomp/vectors" - "math" ) const ( @@ -55,60 +54,6 @@ func CheckCollision( panic("GJK infinite loop") } -func EPA( - a, b AnyCollider, - transformA, transformB *stdcomponents.Transform2d, - simplex *Simplex2d, -) (vectors.Vec2, float32) { - var minIndex int = 0 - var minDistance float32 = float32(math.MaxFloat32) - var minNormal vectors.Vec2 - var polytope = simplex.toPolytope(make([]vectors.Vec2, 0, 6)) - - for range maxItterations { - for i := 0; i < len(polytope); i++ { - j := (i + 1) % len(polytope) - a := polytope[i] - b := polytope[j] - - edge := b.Sub(a) - - // normal := edge.Cross(a.ToVec3()).Cross(edge).ToVec2().Normalize() - normal := edge.Normal().Normalize() - distance := a.Dot(normal) - - if distance < 0 { - normal = normal.Neg() - distance = -distance - } - - if distance < minDistance { - minDistance = distance - minNormal = normal - minIndex = j - } - } - - if minDistance == 0 { - return minNormal, minDistance - } - - minNormal = minNormal.Normalize() - - p := minkowskiSupport2d(a, b, transformA, transformB, minNormal) - normalDot := minNormal.Dot(p) - accuracy := math.Abs(float64(normalDot - minDistance)) - - if accuracy < epaTolerance { - return minNormal, minDistance - } - - polytope = append(polytope[:minIndex], append([]vectors.Vec2{p}, polytope[minIndex:]...)...) - } - - panic("EPA infinite loop") -} - func minkowskiSupport2d(a, b AnyCollider, transformA, transformB *stdcomponents.Transform2d, direction vectors.Vec2) vectors.Vec2 { return a.GetSupport(direction, transformA).Sub(b.GetSupport(direction.Neg(), transformB)) } From 096b09b302e2a4c5191d30e1eecbb838a69e633f Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Mar 2025 17:14:49 +0300 Subject: [PATCH 064/196] add gjk+epa doc links --- pkg/collision/epa.go | 4 ++++ pkg/collision/gjk.go | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/collision/epa.go b/pkg/collision/epa.go index 7862f1f1..6bb9dd53 100644 --- a/pkg/collision/epa.go +++ b/pkg/collision/epa.go @@ -20,6 +20,10 @@ import ( "math" ) +/* +EPA - Expanding Polytope Algorithm +Based on https://dyn4j.org/2010/05/epa-expanding-polytope-algorithm/#epa-alternatives +*/ func EPA( a, b AnyCollider, transformA, transformB *stdcomponents.Transform2d, diff --git a/pkg/collision/gjk.go b/pkg/collision/gjk.go index fc290a4e..8c538454 100644 --- a/pkg/collision/gjk.go +++ b/pkg/collision/gjk.go @@ -19,13 +19,18 @@ import ( const ( maxItterations = 64 - epaTolerance = 0.001 + epaTolerance = 0.00001 ) type AnyCollider interface { GetSupport(direction vectors.Vec2, transform *stdcomponents.Transform2d) vectors.Vec2 } +/* +CheckCollision - GJK, Distance, Closest Points +https://www.youtube.com/watch?v=Qupqu1xe7Io +https://dyn4j.org/2010/04/gjk-distance-closest-points/#gjk-distance +*/ func CheckCollision( a, b AnyCollider, transformA, transformB *stdcomponents.Transform2d, From 7b81b41e91280a96ad45319d2a2fe15ca54b064b Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Mar 2025 18:51:51 +0300 Subject: [PATCH 065/196] fix epa teleportation and panic --- pkg/collision/epa.go | 6 +-- stdcomponents/colliders.go | 26 +++++++++++ stdsystems/collision-detection-bvh.go | 66 +-------------------------- vectors/vector2.go | 5 +- 4 files changed, 33 insertions(+), 70 deletions(-) diff --git a/pkg/collision/epa.go b/pkg/collision/epa.go index 6bb9dd53..fa9a7329 100644 --- a/pkg/collision/epa.go +++ b/pkg/collision/epa.go @@ -57,10 +57,8 @@ func findClosestEdge(polytope []vectors.Vec2) closestEdge { a := polytope[i] b := polytope[j] - edge := b.Sub(a).ToVec3() - oa := a.ToVec3() - - normal := edge.Cross(oa).Cross(edge).ToVec2().Normalize() + edge := b.Sub(a) + normal := edge.Perpendicular().Normalize() distance := normal.Dot(a) if distance < closest.distance { diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 43f08156..e35fa283 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -86,6 +86,15 @@ type CircleCollider struct { Offset vectors.Vec2 } +func (c *CircleCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { + if direction.LengthSquared() == 0 { + return transform.Position + } + radius := c.Radius * transform.Scale.X + dirNorm := direction.Normalize() + return transform.Position.Add(dirNorm.Scale(radius)) +} + type CircleColliderComponentManager = ecs.ComponentManager[CircleCollider] func NewCircleColliderComponentManager() CircleColliderComponentManager { @@ -99,6 +108,23 @@ type PolygonCollider struct { Offset vectors.Vec2 } +func (c *PolygonCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { + maxDot := math.Inf(-1) + var maxVertex vectors.Vec2 + + for _, v := range c.Vertices { + scaled := v.Mul(transform.Scale) + rotated := scaled.Rotate(transform.Rotation) + worldVertex := transform.Position.Add(rotated) + dot := float64(worldVertex.Dot(direction)) + if dot > maxDot { + maxDot = dot + maxVertex = worldVertex + } + } + return maxVertex +} + type PolygonColliderComponentManager = ecs.ComponentManager[PolygonCollider] func NewPolygonColliderComponentManager() PolygonColliderComponentManager { diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index 551d878f..b37bd111 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -20,17 +20,11 @@ import ( "gomp/pkg/ecs" "gomp/stdcomponents" "gomp/vectors" - "math" "runtime" "sync" "time" ) -const ( - EPA_TOLERANCE = 0.00001 - EPA_MAX_ITERATIONS = 64 -) - func NewCollisionDetectionBVHSystem() CollisionDetectionBVHSystem { return CollisionDetectionBVHSystem{ activeCollisions: make(map[CollisionPair]ecs.Entity), @@ -275,72 +269,16 @@ func (s *CollisionDetectionBVHSystem) processExitStates() { } } -// buildBVH constructs hierarchy using sorted morton codes -func (s *CollisionDetectionBVHSystem) buildBVH(entities []ecs.Entity, aabbs []stdcomponents.AABB, mortonCodes []uint32) { - -} - func (s *CollisionDetectionBVHSystem) getGjkCollider(collider *stdcomponents.GenericCollider, entity ecs.Entity) gjk.AnyCollider { switch collider.Shape { case stdcomponents.BoxColliderShape: return s.BoxColliders.Get(entity) case stdcomponents.CircleColliderShape: - // return s.CircleColliders.Get(entity) + return s.CircleColliders.Get(entity) case stdcomponents.PolygonColliderShape: - // return s.PolygonColliders.Get(entity) + return s.PolygonColliders.Get(entity) default: panic("unsupported collider shape") } return nil } - -func (s *CollisionDetectionBVHSystem) circleSupport(circle *stdcomponents.CircleCollider, pos *stdcomponents.Position, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { - if direction.LengthSquared() == 0 { - return pos.XY - } - radius := circle.Radius * scale.X - dirNorm := direction.Normalize() - return pos.XY.Add(dirNorm.Scale(radius)) -} - -func (s *CollisionDetectionBVHSystem) boxSupport(box *stdcomponents.BoxCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { - vertices := [4]vectors.Vec2{ - {X: box.WH.X, Y: box.WH.Y}, - {X: 0, Y: box.WH.Y}, - {X: 0, Y: 0}, - {X: box.WH.X, Y: 0}, - } - - var maxPoint vectors.Vec2 - var maxDistance float32 = -math.MaxFloat32 - - for i := range vertices { - vertex := &vertices[i] - worldVertex := vertex.Sub(box.Offset).Rotate(rot.Angle) - - distance := worldVertex.Dot(direction) - if distance > maxDistance { - maxDistance = distance - maxPoint = worldVertex - } - } - - return maxPoint.Mul(scale).Add(pos.XY) -} - -func (s *CollisionDetectionBVHSystem) polygonSupport(poly *stdcomponents.PolygonCollider, pos *stdcomponents.Position, rot *stdcomponents.Rotation, scale vectors.Vec2, direction vectors.Vec2) vectors.Vec2 { - maxDot := math.Inf(-1) - var maxVertex vectors.Vec2 - - for _, v := range poly.Vertices { - scaled := vectors.Vec2{X: v.X * scale.X, Y: v.Y * scale.Y} - rotated := scaled.Rotate(rot.Angle) - worldVertex := pos.XY.Add(rotated) - dot := float64(worldVertex.Dot(direction)) - if dot > maxDot { - maxDot = dot - maxVertex = worldVertex - } - } - return maxVertex -} diff --git a/vectors/vector2.go b/vectors/vector2.go index dbe2541f..1b149567 100644 --- a/vectors/vector2.go +++ b/vectors/vector2.go @@ -74,10 +74,11 @@ func (v Vec2) Neg() Vec2 { return Vec2{-v.X, -v.Y} } +// Perpendicular - clockwise func (v Vec2) Perpendicular() Vec2 { return Vec2{ - X: -v.Y, - Y: v.X, + X: v.Y, + Y: -v.X, } } From 32d82735bbeb9b7f5b51ee6fa540c5c5f5c94e1c Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Mar 2025 20:27:02 +0300 Subject: [PATCH 066/196] implement circle colliders --- examples/new-api/entities/asteroid.go | 31 +++++++------- examples/new-api/entities/bullet.go | 29 ++++++------- examples/new-api/systems/render-assterodd.go | 16 +++++-- examples/new-api/systems/space-spawner.go | 42 +++++++++---------- examples/new-api/systems/spaceship-intents.go | 19 +++++---- pkg/collision/epa.go | 9 +++- pkg/collision/gjk.go | 5 +-- stdcomponents/colliders.go | 8 +--- stdsystems/collider.go | 22 +++++----- 9 files changed, 93 insertions(+), 88 deletions(-) diff --git a/examples/new-api/entities/asteroid.go b/examples/new-api/entities/asteroid.go index 2b4e003a..64f5b159 100644 --- a/examples/new-api/entities/asteroid.go +++ b/examples/new-api/entities/asteroid.go @@ -27,16 +27,16 @@ import ( ) type CreateAsteroidManagers struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - Velocities *stdcomponents.VelocityComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - Sprites *stdcomponents.SpriteComponentManager - AsteroidTags *components.AsteroidComponentManager - Hp *components.HpComponentManager - RigidBodies *stdcomponents.RigidBodyComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Velocities *stdcomponents.VelocityComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + Sprites *stdcomponents.SpriteComponentManager + AsteroidTags *components.AsteroidComponentManager + Hp *components.HpComponentManager + RigidBodies *stdcomponents.RigidBodyComponentManager } func CreateAsteroid( @@ -64,14 +64,11 @@ func CreateAsteroid( X: velocityX, Y: velocityY, }) - props.BoxColliders.Create(bullet, stdcomponents.BoxCollider{ - WH: vectors.Vec2{ - X: 32, - Y: 32, - }, + props.CircleColliders.Create(bullet, stdcomponents.CircleCollider{ + Radius: 24, Offset: vectors.Vec2{ - X: 16, - Y: 16, + X: 0, + Y: 0, }, Layer: config.EnemyCollisionLayer, Mask: 0, diff --git a/examples/new-api/entities/bullet.go b/examples/new-api/entities/bullet.go index 5c51c841..ba6a7d0c 100644 --- a/examples/new-api/entities/bullet.go +++ b/examples/new-api/entities/bullet.go @@ -26,15 +26,15 @@ import ( ) type CreateBulletManagers struct { - EntityManager *ecs.EntityManager - Positions *stdcomponents.PositionComponentManager - Rotations *stdcomponents.RotationComponentManager - Scales *stdcomponents.ScaleComponentManager - Velocities *stdcomponents.VelocityComponentManager - BoxColliders *stdcomponents.BoxColliderComponentManager - Sprites *stdcomponents.SpriteComponentManager - BulletTags *components.BulletTagComponentManager - Hps *components.HpComponentManager + EntityManager *ecs.EntityManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Velocities *stdcomponents.VelocityComponentManager + CircleColliders *stdcomponents.CircleColliderComponentManager + Sprites *stdcomponents.SpriteComponentManager + BulletTags *components.BulletTagComponentManager + Hps *components.HpComponentManager } func CreateBullet( @@ -61,14 +61,11 @@ func CreateBullet( X: velocityX, Y: velocityY, }) - props.BoxColliders.Create(bullet, stdcomponents.BoxCollider{ - WH: vectors.Vec2{ - X: 16, - Y: 16, - }, + props.CircleColliders.Create(bullet, stdcomponents.CircleCollider{ + Radius: 8, Offset: vectors.Vec2{ - X: 8, - Y: 8, + X: 0, + Y: 0, }, Layer: config.BulletCollisionLayer, Mask: 1< Date: Tue, 25 Mar 2025 21:54:58 +0300 Subject: [PATCH 067/196] update mac build task --- taskfile.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/taskfile.yml b/taskfile.yml index f5916a04..05975f00 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -60,14 +60,14 @@ tasks: env: CGO_ENABLED: 1 cmds: - - env CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -o ./.dist/game-darwin-amd64 examples/new-api/game.go + - env CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -o ./.dist/game-darwin-amd64 -tags opengl43 examples/new-api/game.go - chmod +x ./.dist/game-darwin-amd64 build-darwin-arm64: env: CGO_ENABLED: 1 cmds: - - env CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -o ./.dist/game-darwin-arm64 examples/new-api/game.go + - env CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -o ./.dist/game-darwin-arm64 -tags opengl43 examples/new-api/game.go - chmod +x ./.dist/game-darwin-arm64 build-darwin-universal: From 03746fb46e42fc60002e4192eed0d69870b35141 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 25 Mar 2025 22:04:56 +0300 Subject: [PATCH 068/196] refactor - small changes --- examples/new-api/entities/spaceship.go | 2 +- examples/new-api/systems/spaceship-intents.go | 2 +- stdsystems/collision-detection-bvh.go | 13 ++++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/new-api/entities/spaceship.go b/examples/new-api/entities/spaceship.go index f840b26e..64bcd0fc 100644 --- a/examples/new-api/entities/spaceship.go +++ b/examples/new-api/entities/spaceship.go @@ -102,7 +102,7 @@ func CreateSpaceShip( props.Weapons.Create(spaceShip, components.Weapon{ Damage: 1, - Cooldown: time.Millisecond * 20, + Cooldown: time.Millisecond * 100, CooldownLeft: 0, }) diff --git a/examples/new-api/systems/spaceship-intents.go b/examples/new-api/systems/spaceship-intents.go index 336b9a95..2d916e38 100644 --- a/examples/new-api/systems/spaceship-intents.go +++ b/examples/new-api/systems/spaceship-intents.go @@ -94,7 +94,7 @@ func (s *SpaceshipIntentsSystem) Run(dt time.Duration) { if weapon.CooldownLeft <= 0 { if intent.Fire { - var count int = 360 + var count int = 30 for i := range count { var angle = math.Pi*2/float64(count)*float64(i) + rot.Angle - math.Pi/2 diff --git a/stdsystems/collision-detection-bvh.go b/stdsystems/collision-detection-bvh.go index b37bd111..70312f60 100644 --- a/stdsystems/collision-detection-bvh.go +++ b/stdsystems/collision-detection-bvh.go @@ -60,9 +60,11 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { return } + // Init trees s.trees = make([]bvh.Tree2D, 0, 8) s.treesLookup = make(map[stdcomponents.CollisionLayer]int, 8) + // Fill trees s.AABB.EachEntity(func(entity ecs.Entity) bool { aabb := s.AABB.Get(entity) layer := s.GenericCollider.Get(entity).Layer @@ -79,6 +81,7 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { return true }) + // Build trees wg := new(sync.WaitGroup) wg.Add(len(s.trees)) for i := range s.trees { @@ -137,8 +140,8 @@ func (s *CollisionDetectionBVHSystem) Run(dt time.Duration) { close(collisionChan) <-doneChan // Wait for result collector - } + func (s *CollisionDetectionBVHSystem) Destroy() {} func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity, aabbs []stdcomponents.AABB, collisionChan chan<- CollisionEvent) { @@ -162,7 +165,7 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity for i := range entities[start:end] { entity := entities[i+startIndex] - s.checkEntityCollisions(entity, collisionChan) + s.broadPhase(entity, collisionChan) } }(startIndex, endIndex) } @@ -170,7 +173,7 @@ func (s *CollisionDetectionBVHSystem) findEntityCollisions(entities []ecs.Entity wg.Wait() } -func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, collisionChan chan<- CollisionEvent) { +func (s *CollisionDetectionBVHSystem) broadPhase(entityA ecs.Entity, collisionChan chan<- CollisionEvent) { colliderA := s.GenericCollider.Get(entityA) aabb := s.AABB.Get(entityA) @@ -191,7 +194,7 @@ func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, } colliderB := s.GenericCollider.Get(entityB) - collision, ok := s.checkCollisionGjk(colliderA, colliderB, entityA, entityB) + collision, ok := s.narrowPhase(colliderA, colliderB, entityA, entityB) if ok { collisionChan <- collision } @@ -199,7 +202,7 @@ func (s *CollisionDetectionBVHSystem) checkEntityCollisions(entityA ecs.Entity, } } -func (s *CollisionDetectionBVHSystem) checkCollisionGjk(colliderA, colliderB *stdcomponents.GenericCollider, entityA, entityB ecs.Entity) (e CollisionEvent, ok bool) { +func (s *CollisionDetectionBVHSystem) narrowPhase(colliderA, colliderB *stdcomponents.GenericCollider, entityA, entityB ecs.Entity) (e CollisionEvent, ok bool) { colA := s.getGjkCollider(colliderA, entityA) colB := s.getGjkCollider(colliderB, entityB) posA := s.Positions.Get(entityA) From ab5c1592232569959400162197892570b1778af0 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 26 Mar 2025 07:14:40 +0300 Subject: [PATCH 069/196] gjk - fix circle collider support function --- stdcomponents/colliders.go | 9 +++++++-- vectors/vector2.go | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/stdcomponents/colliders.go b/stdcomponents/colliders.go index 8a0602d8..8038b58c 100644 --- a/stdcomponents/colliders.go +++ b/stdcomponents/colliders.go @@ -87,8 +87,13 @@ type CircleCollider struct { } func (c *CircleCollider) GetSupport(direction vectors.Vec2, transform *Transform2d) vectors.Vec2 { - scalesRadius := transform.Scale.Scale(c.Radius) - return transform.Position.Add(direction.Normalize().Mul(scalesRadius)) + angle := direction.Angle() + rotatedRadius := vectors.Vec2{ + X: c.Radius * float32(math.Cos(angle)), + Y: c.Radius * float32(math.Sin(angle)), + } + scaledOffset := c.Offset.Mul(transform.Scale) + return transform.Position.Sub(scaledOffset).Add(rotatedRadius.Mul(transform.Scale)) } type CircleColliderComponentManager = ecs.ComponentManager[CircleCollider] diff --git a/vectors/vector2.go b/vectors/vector2.go index 1b149567..ade5c963 100644 --- a/vectors/vector2.go +++ b/vectors/vector2.go @@ -50,6 +50,10 @@ func (v Vec2) Scale(scalar float32) Vec2 { return Vec2{v.X * scalar, v.Y * scalar} } +func (v Vec2) Angle() Radians { + return math.Atan2(float64(v.Y), float64(v.X)) +} + func (v Vec2) Length() float32 { return float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y))) } From 0bed52200067c19774e485a9f6902533a22b9dd7 Mon Sep 17 00:00:00 2001 From: thearturca Date: Wed, 26 Mar 2025 10:12:33 +0300 Subject: [PATCH 070/196] feat(audio): added some self-made SFX for asterodds --- examples/new-api/assets/assets.go | 37 +++++++++++++- examples/new-api/assets/damage_sound.wav | Bin 0 -> 231572 bytes examples/new-api/assets/fly_sound.wav | Bin 0 -> 926144 bytes examples/new-api/assets/gun_sound.wav | Bin 0 -> 396944 bytes examples/new-api/instances/system-list.go | 6 ++- examples/new-api/scenes/assterodd-scene.go | 2 + examples/new-api/systems/audio.go | 45 ++++++++++++++++++ examples/new-api/systems/collision-handler.go | 7 +++ examples/new-api/systems/spaceship-intents.go | 20 ++++++++ 9 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 examples/new-api/assets/damage_sound.wav create mode 100644 examples/new-api/assets/fly_sound.wav create mode 100644 examples/new-api/assets/gun_sound.wav create mode 100644 examples/new-api/systems/audio.go diff --git a/examples/new-api/assets/assets.go b/examples/new-api/assets/assets.go index 92b82f34..4cce7e3b 100644 --- a/examples/new-api/assets/assets.go +++ b/examples/new-api/assets/assets.go @@ -9,14 +9,17 @@ package assets import ( "embed" "fmt" - rl "github.com/gen2brain/raylib-go/raylib" - "github.com/negrel/assert" "gomp" "image/png" "log" + "strings" + + rl "github.com/gen2brain/raylib-go/raylib" + "github.com/negrel/assert" ) //go:embed *.png +//go:embed *.wav var fs embed.FS var Textures = gomp.CreateAssetLibrary( @@ -43,3 +46,33 @@ var Textures = gomp.CreateAssetLibrary( rl.UnloadTexture(*asset) }, ) + +var Audio = gomp.CreateAssetLibrary( + func(path string) rl.Sound { + file, err := fs.ReadFile(path) + + if err != nil { + log.Panic("Error opening file") + } + + fileTypeIndex := strings.LastIndex(path, ".") + + fileType := ".wav" + + if fileTypeIndex != -1 { + fileType = path[fileTypeIndex:] + } + + wave := rl.LoadWaveFromMemory(fileType, file, int32(len(file))) + + sound := rl.LoadSoundFromWave(wave) + rl.UnloadWave(wave) + + assert.True(rl.IsSoundValid(sound), "Error loading sound") + + return sound + }, + func(path string, asset *rl.Sound) { + rl.UnloadSound(*asset) + }, +) diff --git a/examples/new-api/assets/damage_sound.wav b/examples/new-api/assets/damage_sound.wav new file mode 100644 index 0000000000000000000000000000000000000000..768352e7b5bcf74737e60911958516392c9a1393 GIT binary patch literal 231572 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 woXo-1bPLix@m5nad}Qiwv1$}JJy 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 literal 0 HcmV?d00001 diff --git a/examples/new-api/assets/star.png b/examples/new-api/assets/star.png new file mode 100644 index 0000000000000000000000000000000000000000..9fd0431c03e1478f015112fdc010f33d395a6671 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|qC8z3LnNjq zCpa+8jEwwmym`|<3bcj6)78&qol`;+ E0QLkmUjP6A literal 0 HcmV?d00001 diff --git a/examples/new-api/components/components.go b/examples/new-api/components/components.go new file mode 100644 index 00000000..50dc67db --- /dev/null +++ b/examples/new-api/components/components.go @@ -0,0 +1,22 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +const ( + INVALID_ID ecs.ComponentID = iota + HEALTH_ID +) diff --git a/examples/new-api/components/hp.go b/examples/new-api/components/hp.go new file mode 100644 index 00000000..8cd00baa --- /dev/null +++ b/examples/new-api/components/hp.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +type Health struct { + Hp, MaxHp int32 +} + +type HealthComponentManager = ecs.ComponentManager[Health] + +func NewHealthComponentManager(world *ecs.World) *ecs.ComponentManager[Health] { + return ecs.NewComponentManager[Health](world, HEALTH_ID) +} diff --git a/examples/new-api/desktop-components.go b/examples/new-api/desktop-components.go new file mode 100644 index 00000000..2abb44e9 --- /dev/null +++ b/examples/new-api/desktop-components.go @@ -0,0 +1,52 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +func NewDesktopComponents(world *ecs.World) *desktopComponents { + return &desktopComponents{ + position: stdcomponents.NewPositionComponentManager(world), + rotation: stdcomponents.NewRotationComponentManager(world), + scale: stdcomponents.NewScaleComponentManager(world), + flip: stdcomponents.NewFlipComponentManager(world), + sprite: stdcomponents.NewSpriteComponentManager(world), + spriteSheet: stdcomponents.NewSpriteSheetComponentManager(world), + spriteMatrix: stdcomponents.NewSpriteMatrixComponentManager(world), + tint: stdcomponents.NewTintComponentManager(world), + animationPlayer: stdcomponents.NewAnimationPlayerComponentManager(world), + animationState: stdcomponents.NewAnimationStateComponentManager(world), + textureRender: stdcomponents.NewTextureRenderComponentManager(world), + network: stdcomponents.NewNetworkComponentManager(world), + } +} + +type desktopComponents struct { + position *stdcomponents.PositionComponentManager + rotation *ecs.ComponentManager[stdcomponents.Rotation] + scale *ecs.ComponentManager[stdcomponents.Scale] + flip *ecs.ComponentManager[stdcomponents.Flip] + sprite *ecs.ComponentManager[stdcomponents.Sprite] + spriteSheet *ecs.ComponentManager[stdcomponents.SpriteSheet] + spriteMatrix *ecs.ComponentManager[stdcomponents.SpriteMatrix] + tint *ecs.ComponentManager[stdcomponents.Tint] + animationPlayer *ecs.ComponentManager[stdcomponents.AnimationPlayer] + animationState *ecs.ComponentManager[stdcomponents.AnimationState] + textureRender *ecs.ComponentManager[stdcomponents.TextureRender] + network *ecs.ComponentManager[stdcomponents.Network] +} diff --git a/examples/new-api/desktop-systems.go b/examples/new-api/desktop-systems.go new file mode 100644 index 00000000..15825bec --- /dev/null +++ b/examples/new-api/desktop-systems.go @@ -0,0 +1,186 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "gomp/examples/new-api/assets" + "gomp/examples/new-api/systems" + "gomp/pkg/ecs" + "gomp/stdsystems" + "time" +) + +func NewDesktopSystems(world *ecs.World, components *desktopComponents) *desktopSystems { + return &desktopSystems{ + player: systems.NewPlayerSystem(world, components.spriteMatrix, components.position, components.rotation, components.scale, components.animationPlayer, components.animationState, components.tint, components.flip), + + debug: stdsystems.NewDebugSystem(), + + network: stdsystems.NewNetworkSystem(), + networkReceive: stdsystems.NewNetworkReceiveSystem(), + networkSend: stdsystems.NewNetworkSendSystem(world, components.position, components.rotation, components.flip), + + animationSpriteMatrix: stdsystems.NewAnimationSpriteMatrixSystem(world, components.animationPlayer, components.animationState, components.spriteMatrix), + animationPlayer: stdsystems.NewAnimationPlayerSystem(components.animationPlayer), + + textureRenderSprite: stdsystems.NewTextureRenderSpriteSystem(components.sprite, components.textureRender), + textureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(components.spriteSheet, components.textureRender), + textureRenderMatrix: stdsystems.NewTextureRenderMatrixSystem(components.spriteMatrix, components.textureRender, components.animationState), + + textureRenderAnimation: stdsystems.NewTextureRenderAnimationSystem(components.animationPlayer, components.textureRender), + textureRenderFlip: stdsystems.NewTextureRenderFlipSystem(components.flip, components.textureRender), + textureRenderPosition: stdsystems.NewTextureRenderPositionSystem(components.position, components.textureRender), + textureRenderRotation: stdsystems.NewTextureRenderRotationSystem(components.rotation, components.textureRender), + textureRenderScale: stdsystems.NewTextureRenderScaleSystem(components.scale, components.textureRender), + textureRenderTint: stdsystems.NewTextureRenderTintSystem(components.tint, components.textureRender), + + assetLib: stdsystems.NewAssetLibSystem([]ecs.AnyAssetLibrary{assets.Textures}), + render: stdsystems.NewRenderSystem(world, components.textureRender), + } +} + +type desktopSystems struct { + player *systems.PlayerSystem + + debug *stdsystems.DebugSystem + // Network + network *stdsystems.NetworkSystem + networkReceive *stdsystems.NetworkReceiveSystem + networkSend *stdsystems.NetworkSendSystem + // Animation + animationSpriteMatrix *stdsystems.AnimationSpriteMatrixSystem + animationPlayer *stdsystems.AnimationPlayerSystem + // Prerender init + textureRenderSprite *stdsystems.TextureRenderSpriteSystem + textureRenderSpriteSheet *stdsystems.TextureRenderSpriteSheetSystem + textureRenderMatrix *stdsystems.TextureRenderMatrixSystem + // Prerender fill + textureRenderAnimation *stdsystems.TextureRenderAnimationSystem + textureRenderFlip *stdsystems.TextureRenderFlipSystem + textureRenderPosition *stdsystems.TextureRenderPositionSystem + textureRenderRotation *stdsystems.TextureRenderRotationSystem + textureRenderScale *stdsystems.TextureRenderScaleSystem + textureRenderTint *stdsystems.TextureRenderTintSystem + // Render + assetLib *stdsystems.AssetLibSystem + render *stdsystems.RenderSystem +} + +func (s *desktopSystems) Init() { + // Network receive + s.network.Init() + s.networkReceive.Init() + + // Network patches + s.networkSend.Init() + + // Scenes + s.player.Init() + + // Animation + s.animationSpriteMatrix.Init() + s.animationPlayer.Init() + + // Prerender init + s.textureRenderSprite.Init() + s.textureRenderSpriteSheet.Init() + s.textureRenderMatrix.Init() + + // Prerender fill + s.textureRenderAnimation.Init() + s.textureRenderFlip.Init() + s.textureRenderPosition.Init() + s.textureRenderRotation.Init() + s.textureRenderScale.Init() + s.textureRenderTint.Init() + + // Render + s.render.Init() + s.debug.Init() + s.assetLib.Init() +} + +func (s *desktopSystems) Update(dt time.Duration) { + // Network receive + s.network.Run(dt) + s.networkReceive.Run(dt) + + // Scenes + s.player.Run(dt) + + // Animation + s.animationSpriteMatrix.Run(dt) + s.animationPlayer.Run(dt) + + // Prerender init + s.textureRenderSprite.Run(dt) + s.textureRenderSpriteSheet.Run(dt) + s.textureRenderMatrix.Run(dt) + + // Prerender fill + s.textureRenderAnimation.Run(dt) + s.textureRenderFlip.Run(dt) + s.textureRenderPosition.Run(dt) + s.textureRenderRotation.Run(dt) + s.textureRenderScale.Run(dt) + s.textureRenderTint.Run(dt) + + // Render + s.assetLib.Run(dt) + s.debug.Run(dt) + s.render.Run(dt) +} + +func (s *desktopSystems) FixedUpdate(dt time.Duration) { + // Scenes + s.player.Run(dt) + + // Network send + s.networkSend.Run(dt) +} + +func (s *desktopSystems) Destroy() { + // Network intents + s.network.Destroy() + s.networkReceive.Destroy() + + // Scenes + s.player.Destroy() + + // Network patches + s.networkSend.Destroy() + + // Animation + s.animationSpriteMatrix.Destroy() + s.animationPlayer.Destroy() + + // Prerender init + s.textureRenderSprite.Destroy() + s.textureRenderSpriteSheet.Destroy() + s.textureRenderMatrix.Destroy() + + // Prerender fill + s.textureRenderAnimation.Destroy() + s.textureRenderFlip.Destroy() + s.textureRenderPosition.Destroy() + s.textureRenderRotation.Destroy() + s.textureRenderScale.Destroy() + s.textureRenderTint.Destroy() + + // Render + s.debug.Destroy() + s.assetLib.Destroy() + s.render.Destroy() +} diff --git a/examples/new-api/desktop.go b/examples/new-api/desktop.go new file mode 100644 index 00000000..cb20b91c --- /dev/null +++ b/examples/new-api/desktop.go @@ -0,0 +1,31 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import ( + "gomp" + "gomp/pkg/ecs" +) + +func main() { + world := ecs.CreateWorld("main") + defer world.Destroy() + + components := NewDesktopComponents(world) + systems := NewDesktopSystems(world, components) + + engine := gomp.NewEngine(world, components, systems) + engine.Run(60) +} diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go new file mode 100644 index 00000000..14b29141 --- /dev/null +++ b/examples/new-api/entities/player.go @@ -0,0 +1,116 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package entities + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/assets" + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +const ( + PlayerStateIdle stdcomponents.AnimationState = iota + PlayerStateWalk + PlayerStateJump + PlayerStateFall + PlayerStateAttack + PlayerStateHurt + PlayerStateDie +) + +type Player struct { + ecs.Entity + Position *stdcomponents.Position + Rotation *stdcomponents.Rotation + Scale *stdcomponents.Scale + SpriteMatrix *stdcomponents.SpriteMatrix + Tint *stdcomponents.Tint + AnimationPlayer *stdcomponents.AnimationPlayer + AnimationState *stdcomponents.AnimationState + Flip *stdcomponents.Flip +} + +var playerSpriteMatrix = stdcomponents.SpriteMatrix{ + Texture: assets.Textures.Get("examples/new-api/assets/milansheet.png"), + Origin: rl.Vector2{X: 0.5, Y: 0.5}, + FPS: 12, + Animations: []stdcomponents.SpriteMatrixAnimation{ + { + Name: "idle", + Frame: rl.Rectangle{X: 0, Y: 0, Width: 96, Height: 128}, + NumOfFrames: 1, + Vertical: false, + Loop: true, + }, + { + Name: "walk", + Frame: rl.Rectangle{X: 0, Y: 512, Width: 96, Height: 128}, + NumOfFrames: 8, + Vertical: false, + Loop: true, + }, + { + Name: "jump", + Frame: rl.Rectangle{X: 96, Y: 0, Width: 96, Height: 128}, + NumOfFrames: 1, + Vertical: false, + Loop: false, + }, + }, +} + +func CreatePlayer( + world *ecs.World, + spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, + positions *stdcomponents.PositionComponentManager, + rotations *stdcomponents.RotationComponentManager, + scales *stdcomponents.ScaleComponentManager, + animationPlayers *stdcomponents.AnimationPlayerComponentManager, + animationStates *stdcomponents.AnimationStateComponentManager, + tints *stdcomponents.TintComponentManager, + flips *stdcomponents.FlipComponentManager, +) (player Player) { + // Creating new player + + entity := world.CreateEntity("player") + player.Entity = entity + + // Adding position component + t := stdcomponents.Position{} + player.Position = positions.Create(entity, t) + + // Adding rotation component + rotation := stdcomponents.Rotation{} + player.Rotation = rotations.Create(entity, rotation) + + // Adding scale component + scale := stdcomponents.Scale{ + X: 1, + Y: 1, + } + player.Scale = scales.Create(entity, scale) + + // Adding Tint component + tint := stdcomponents.Tint{R: 255, G: 255, B: 255, A: 255} + player.Tint = tints.Create(entity, tint) + + // Adding sprite matrix component + player.SpriteMatrix = spriteMatrixes.Create(entity, playerSpriteMatrix) + + // Adding animation player component + animation := stdcomponents.AnimationPlayer{} + player.AnimationPlayer = animationPlayers.Create(entity, animation) + + // Adding Animation state component + player.AnimationState = animationStates.Create(entity, PlayerStateWalk) + + // Adding Flip component + player.Flip = flips.Create(entity, stdcomponents.Flip{}) + + return player +} diff --git a/examples/new-api/systems/color.go b/examples/new-api/systems/color.go new file mode 100644 index 00000000..64b50561 --- /dev/null +++ b/examples/new-api/systems/color.go @@ -0,0 +1,44 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package systems + +import ( + "gomp/examples/raylib-ecs/components" + ecs2 "gomp/pkg/ecs" + "image/color" +) + +type colorController struct { + baseColor color.RGBA +} + +func (s *colorController) Init(world *ecs2.World) { + s.baseColor = color.RGBA{25, 220, 200, 255} +} +func (s *colorController) Update(world *ecs2.World) {} +func (s *colorController) FixedUpdate(world *ecs2.World) { + sprites := components.SpriteService.GetManager(world) + hps := components.HealthService.GetManager(world) + + sprites.AllParallel(func(entity ecs2.Entity, sprite *components.Sprite) bool { + hp := hps.Get(entity) + if hp == nil { + return true + } + + hpPercentage := float32(hp.Hp) / float32(hp.MaxHp) + + sprite.Tint = color.RGBA{ + uint8(hpPercentage * float32(s.baseColor.R)), + uint8(hpPercentage * float32(s.baseColor.G)), + uint8(hpPercentage * float32(s.baseColor.B)), + s.baseColor.A, + } + return true + }) +} +func (s *colorController) Destroy(world *ecs2.World) {} diff --git a/examples/new-api/systems/hp.go b/examples/new-api/systems/hp.go new file mode 100644 index 00000000..2fe51b95 --- /dev/null +++ b/examples/new-api/systems/hp.go @@ -0,0 +1,31 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package systems + +import ( + "gomp/examples/raylib-ecs/components" + ecs2 "gomp/pkg/ecs" +) + +type hpController struct{} + +func (s *hpController) Init(world *ecs2.World) {} +func (s *hpController) Update(world *ecs2.World) {} +func (s *hpController) FixedUpdate(world *ecs2.World) { + healths := components.HealthService.GetManager(world) + + healths.All(func(entity ecs2.Entity, h *components.Health) bool { + h.Hp-- + + if h.Hp <= 0 { + world.DestroyEntity(entity) + } + + return true + }) +} +func (s *hpController) Destroy(world *ecs2.World) {} diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go new file mode 100644 index 00000000..f9ea9c98 --- /dev/null +++ b/examples/new-api/systems/player.go @@ -0,0 +1,86 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package systems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/examples/new-api/entities" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +type PlayerSystem struct { + world *ecs.World + player entities.Player + spriteMatrixes *stdcomponents.SpriteMatrixComponentManager + positions *stdcomponents.PositionComponentManager + rotations *stdcomponents.RotationComponentManager + scales *stdcomponents.ScaleComponentManager + animationPlayers *stdcomponents.AnimationPlayerComponentManager + animationStates *stdcomponents.AnimationStateComponentManager + tints *stdcomponents.TintComponentManager + flips *stdcomponents.FlipComponentManager +} + +func (s *PlayerSystem) Init() { + s.player = entities.CreatePlayer(s.world, s.spriteMatrixes, s.positions, s.rotations, s.scales, s.animationPlayers, s.animationStates, s.tints, s.flips) + s.player.Position.X = 100 + s.player.Position.Y = 100 +} +func (s *PlayerSystem) Run(dt time.Duration) { + animationState := s.animationStates.Get(s.player.Entity) + + if rl.IsKeyDown(rl.KeySpace) { + *animationState = entities.PlayerStateJump + } else { + *animationState = entities.PlayerStateIdle + if rl.IsKeyDown(rl.KeyD) { + *animationState = entities.PlayerStateWalk + s.player.Position.X++ + s.player.Flip.X = false + } + if rl.IsKeyDown(rl.KeyA) { + *animationState = entities.PlayerStateWalk + s.player.Position.X-- + s.player.Flip.X = true + } + if rl.IsKeyDown(rl.KeyW) { + *animationState = entities.PlayerStateWalk + s.player.Position.Y-- + } + if rl.IsKeyDown(rl.KeyS) { + *animationState = entities.PlayerStateWalk + s.player.Position.Y++ + } + } +} +func (s *PlayerSystem) Destroy() {} + +func NewPlayerSystem( + world *ecs.World, + spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, + positions *stdcomponents.PositionComponentManager, + rotations *stdcomponents.RotationComponentManager, + scales *stdcomponents.ScaleComponentManager, + animationPlayers *stdcomponents.AnimationPlayerComponentManager, + animationStates *stdcomponents.AnimationStateComponentManager, + tints *stdcomponents.TintComponentManager, + flips *stdcomponents.FlipComponentManager, +) *PlayerSystem { + return &PlayerSystem{ + world: world, + spriteMatrixes: spriteMatrixes, + positions: positions, + rotations: rotations, + scales: scales, + animationPlayers: animationPlayers, + animationStates: animationStates, + tints: tints, + flips: flips, + } +} diff --git a/examples/new-api/systems/spawn.go b/examples/new-api/systems/spawn.go new file mode 100644 index 00000000..304dc6c2 --- /dev/null +++ b/examples/new-api/systems/spawn.go @@ -0,0 +1,89 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package systems + +import ( + "gomp/examples/raylib-ecs/assets" + "gomp/examples/raylib-ecs/components" + "gomp/pkg/ecs" + "math/rand" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +type spawnController struct { + pprofEnabled bool +} + +const ( + minHpPercentage = 20 + minMaxHp = 500 + maxMaxHp = 2000 +) + +func (s *spawnController) Init(world *ecs.World) {} +func (s *spawnController) Update(world *ecs.World) { + sprites := components.SpriteService.GetManager(world) + healths := components.HealthService.GetManager(world) + positions := components.PositionService.GetManager(world) + rotations := components.RotationService.GetManager(world) + scales := components.ScaleService.GetManager(world) + + if rl.IsKeyDown(rl.KeySpace) { + for range rand.Intn(10000) { + if world.Size() > 100_000_000 { + break + } + + newCreature := world.CreateEntity("Creature") + + // Adding position component + t := components.Position{ + X: float32(rand.Int31n(800)), + Y: float32(rand.Int31n(600)), + } + positions.Create(newCreature, t) + + // Adding rotation component + rotation := components.Rotation{ + Angle: float32(rand.Int31n(360)), + } + rotations.Create(newCreature, rotation) + + // Adding scale component + scale := components.Scale{ + X: 2, + Y: 2, + } + scales.Create(newCreature, scale) + + // Adding HP component + maxHp := minMaxHp + rand.Int31n(maxMaxHp-minMaxHp) + hp := int32(float32(maxHp) * float32(minHpPercentage+rand.Int31n(100-minHpPercentage)) / 100) + h := components.Health{ + Hp: hp, + MaxHp: maxHp, + } + healths.Create(newCreature, h) + + texture := assets.Textures.Get("assets/star.png") + + // Adding sprite component + c := components.Sprite{ + Origin: rl.Vector2{X: 0.5, Y: 0.5}, + Texture: texture, + Frame: rl.Rectangle{X: 0, Y: 0, Width: float32(texture.Width), Height: float32(texture.Height)}, + } + + sprites.Create(newCreature, c) + } + } +} +func (s *spawnController) FixedUpdate(world *ecs.World) { +} + +func (s *spawnController) Destroy(world *ecs.World) {} diff --git a/pkg/ecs/component.go b/pkg/ecs/component.go index 33aba089..58c2c2f8 100644 --- a/pkg/ecs/component.go +++ b/pkg/ecs/component.go @@ -82,6 +82,27 @@ func (c *ComponentService[T]) getId() ComponentID { // Service // ================ +func NewComponentManager[T any](world *World, id ComponentID) *ComponentManager[T] { + newManager := ComponentManager[T]{ + mx: new(sync.Mutex), + + components: NewPagedArray[T](), + entities: NewPagedArray[Entity](), + lookup: NewPagedMap[Entity, int32](), + + maskComponent: world.entityComponentMask, + id: id, + isInitialized: true, + + TrackChanges: false, + createdEntities: NewPagedArray[Entity](), + patchedEntities: NewPagedArray[Entity](), + deletedEntities: NewPagedArray[Entity](), + } + + return &newManager +} + type ComponentManager[T any] struct { mx *sync.Mutex diff --git a/pkg/ecs/component_test.go b/pkg/ecs/component_test.go index 8b156a0c..95d25f55 100644 --- a/pkg/ecs/component_test.go +++ b/pkg/ecs/component_test.go @@ -34,7 +34,7 @@ func PrepareWorld(description string, system AnySystemServicePtr) *World { system, ) - return &world + return world } func InitPixelComponent(pixelComponent *ComponentManager[pixel], world *World) { diff --git a/pkg/ecs/ecs.go b/pkg/ecs/ecs.go index 9a906718..acae8aa2 100644 --- a/pkg/ecs/ecs.go +++ b/pkg/ecs/ecs.go @@ -17,7 +17,7 @@ const ( PREALLOC_DELETED_ENTITIES uint32 = 1 << 10 ) -func CreateWorld(title string) World { +func CreateWorld(title string) *World { id := generateWorldID() maskSet := NewSparseSet[ComponentBitArray256, Entity]() @@ -33,7 +33,7 @@ func CreateWorld(title string) World { components: make(map[ComponentID]AnyComponentManagerPtr), } - return world + return &world } func CreateComponentService[T any](id ComponentID) ComponentService[T] { diff --git a/stdcomponents/animation-player.go b/stdcomponents/animation-player.go new file mode 100644 index 00000000..06235809 --- /dev/null +++ b/stdcomponents/animation-player.go @@ -0,0 +1,39 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" + "time" +) + +type AnimationPlayer struct { + First uint8 + Last uint8 + Current uint8 + Speed float32 + Loop bool + Vertical bool + ElapsedTime time.Duration + FrameDuration time.Duration + State AnimationState + IsInitialized bool +} + +type AnimationPlayerComponentManager = ecs.ComponentManager[AnimationPlayer] + +func NewAnimationPlayerComponentManager(world *ecs.World) *ecs.ComponentManager[AnimationPlayer] { + return ecs.NewComponentManager[AnimationPlayer](world, ANIMATION_PLAYER_ID) +} diff --git a/stdcomponents/animation-state.go b/stdcomponents/animation-state.go new file mode 100644 index 00000000..2b5c27bb --- /dev/null +++ b/stdcomponents/animation-state.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" +) + +type AnimationState int32 + +type AnimationStateComponentManager = ecs.ComponentManager[AnimationState] + +func NewAnimationStateComponentManager(world *ecs.World) *ecs.ComponentManager[AnimationState] { + return ecs.NewComponentManager[AnimationState](world, ANIMATION_STATE_ID) +} diff --git a/stdcomponents/components.go b/stdcomponents/components.go new file mode 100644 index 00000000..83247867 --- /dev/null +++ b/stdcomponents/components.go @@ -0,0 +1,32 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" +) + +const ( + INVALID_ID ecs.ComponentID = iota + 128 + COLOR_ID + POSITION_ID + ROTATION_ID + SCALE_ID + FLIP_ID + HEALTH_ID + DESTROY_ID + SPRITE_ID + SPRITE_SHEET_ID + SPRITE_MATRIX_ID + TEXTURE_RENDER_ID + ANIMATION_ID + ANIMATION_PLAYER_ID + ANIMATION_STATE_ID + TINT_ID + SPRITE_ANIMATOR_ID + NETWORK_ID +) diff --git a/stdcomponents/flip.go b/stdcomponents/flip.go new file mode 100644 index 00000000..655f9cb0 --- /dev/null +++ b/stdcomponents/flip.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type Flip struct { + X, Y bool +} + +type FlipComponentManager = ecs.ComponentManager[Flip] + +func NewFlipComponentManager(world *ecs.World) *ecs.ComponentManager[Flip] { + return ecs.NewComponentManager[Flip](world, FLIP_ID) +} diff --git a/stdcomponents/network.go b/stdcomponents/network.go new file mode 100644 index 00000000..78d8452f --- /dev/null +++ b/stdcomponents/network.go @@ -0,0 +1,28 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type NetworkId int32 +type Network struct { + Id NetworkId + PatchIn []byte + PatchOut []byte +} + +func NewNetworkComponentManager(world *ecs.World) *ecs.ComponentManager[Network] { + return ecs.NewComponentManager[Network](world, NETWORK_ID) +} diff --git a/stdcomponents/position.go b/stdcomponents/position.go new file mode 100644 index 00000000..6c3f279a --- /dev/null +++ b/stdcomponents/position.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type Position struct { + X, Y float32 +} + +type PositionComponentManager = ecs.ComponentManager[Position] + +func NewPositionComponentManager(world *ecs.World) *ecs.ComponentManager[Position] { + return ecs.NewComponentManager[Position](world, POSITION_ID) +} diff --git a/stdcomponents/rotation.go b/stdcomponents/rotation.go new file mode 100644 index 00000000..11af4f3f --- /dev/null +++ b/stdcomponents/rotation.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type Rotation struct { + Angle float32 +} + +type RotationComponentManager = ecs.ComponentManager[Rotation] + +func NewRotationComponentManager(world *ecs.World) *ecs.ComponentManager[Rotation] { + return ecs.NewComponentManager[Rotation](world, ROTATION_ID) +} diff --git a/stdcomponents/scale.go b/stdcomponents/scale.go new file mode 100644 index 00000000..9a4d96c9 --- /dev/null +++ b/stdcomponents/scale.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type Scale struct { + X, Y float32 +} + +type ScaleComponentManager = ecs.ComponentManager[Scale] + +func NewScaleComponentManager(world *ecs.World) *ecs.ComponentManager[Scale] { + return ecs.NewComponentManager[Scale](world, SCALE_ID) +} diff --git a/stdcomponents/sprite-matrix.go b/stdcomponents/sprite-matrix.go new file mode 100644 index 00000000..e6c0bb42 --- /dev/null +++ b/stdcomponents/sprite-matrix.go @@ -0,0 +1,41 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" +) + +type SpriteMatrixAnimation struct { + Name string + Frame rl.Rectangle + NumOfFrames uint8 + Vertical bool + Loop bool +} + +type SpriteMatrix struct { + Texture *rl.Texture2D + Origin rl.Vector2 + FPS int32 + Animations []SpriteMatrixAnimation +} + +type SpriteMatrixComponentManager = ecs.ComponentManager[SpriteMatrix] + +func NewSpriteMatrixComponentManager(world *ecs.World) *ecs.ComponentManager[SpriteMatrix] { + return ecs.NewComponentManager[SpriteMatrix](world, SPRITE_MATRIX_ID) +} diff --git a/stdcomponents/sprite-sheet.go b/stdcomponents/sprite-sheet.go new file mode 100644 index 00000000..98c64d81 --- /dev/null +++ b/stdcomponents/sprite-sheet.go @@ -0,0 +1,35 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" +) + +type SpriteSheet struct { + Texture *rl.Texture2D + Frame rl.Rectangle + Origin rl.Vector2 + NumOfFrames int32 + FPS int32 + Vertical bool +} + +type SpriteSheetComponentManager = ecs.ComponentManager[SpriteSheet] + +func NewSpriteSheetComponentManager(world *ecs.World) *ecs.ComponentManager[SpriteSheet] { + return ecs.NewComponentManager[SpriteSheet](world, SPRITE_SHEET_ID) +} diff --git a/stdcomponents/sprite.go b/stdcomponents/sprite.go new file mode 100644 index 00000000..197d9ce5 --- /dev/null +++ b/stdcomponents/sprite.go @@ -0,0 +1,34 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "image/color" +) + +type Sprite struct { + Texture *rl.Texture2D + Frame rl.Rectangle + Origin rl.Vector2 + Tint color.RGBA +} + +type SpriteComponentManager = ecs.ComponentManager[Sprite] + +func NewSpriteComponentManager(world *ecs.World) *ecs.ComponentManager[Sprite] { + return ecs.NewComponentManager[Sprite](world, SPRITE_ID) +} diff --git a/stdcomponents/texture-render.go b/stdcomponents/texture-render.go new file mode 100644 index 00000000..81e24dc5 --- /dev/null +++ b/stdcomponents/texture-render.go @@ -0,0 +1,36 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "image/color" +) + +type TextureRender struct { + Texture *rl.Texture2D + Frame rl.Rectangle + Origin rl.Vector2 + Tint color.RGBA + Dest rl.Rectangle + Rotation float32 +} + +type TextureRenderComponentManager = ecs.ComponentManager[TextureRender] + +func NewTextureRenderComponentManager(world *ecs.World) *ecs.ComponentManager[TextureRender] { + return ecs.NewComponentManager[TextureRender](world, TEXTURE_RENDER_ID) +} diff --git a/stdcomponents/tint.go b/stdcomponents/tint.go new file mode 100644 index 00000000..a15dd9e1 --- /dev/null +++ b/stdcomponents/tint.go @@ -0,0 +1,28 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import ( + "gomp/pkg/ecs" + "image/color" +) + +type Tint = color.RGBA + +type TintComponentManager = ecs.ComponentManager[Tint] + +func NewTintComponentManager(world *ecs.World) *ecs.ComponentManager[Tint] { + return ecs.NewComponentManager[Tint](world, TINT_ID) +} diff --git a/stdsystems/animation-player.go b/stdsystems/animation-player.go new file mode 100644 index 00000000..e34cb03f --- /dev/null +++ b/stdsystems/animation-player.go @@ -0,0 +1,67 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "fmt" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" + + "github.com/negrel/assert" +) + +type AnimationPlayerSystem struct { + animationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] +} + +func NewAnimationPlayerSystem(animationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer]) *AnimationPlayerSystem { + return &AnimationPlayerSystem{ + animationPlayers: animationPlayers, + } +} + +func (s *AnimationPlayerSystem) Init() {} +func (s *AnimationPlayerSystem) Run(dt time.Duration) { + s.animationPlayers.AllDataParallel(func(animation *stdcomponents.AnimationPlayer) bool { + animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond + + assert.True(animation.FrameDuration > 0, fmt.Errorf("frame duration must be greater than 0 (got %v)", animation.FrameDuration)) + + // Check if animation is playing backwards + if animation.Speed < 0 { + for animation.ElapsedTime <= 0 { + animation.ElapsedTime += animation.FrameDuration + animation.Current-- + + if animation.Current < animation.First { + if animation.Loop { + animation.Current = animation.Last + } else { + animation.Current = animation.First + } + } + } + } else { + for animation.ElapsedTime >= animation.FrameDuration { + animation.ElapsedTime -= animation.FrameDuration + animation.Current++ + + if animation.Current > animation.Last { + if animation.Loop { + animation.Current = animation.First + } else { + animation.Current = animation.Last + } + } + } + } + + return true + }) +} +func (s *AnimationPlayerSystem) Destroy() {} diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go new file mode 100644 index 00000000..df99b186 --- /dev/null +++ b/stdsystems/animation-spritematrix.go @@ -0,0 +1,67 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewAnimationSpriteMatrixSystem(world *ecs.World, + animationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer], + animationStates *ecs.ComponentManager[stdcomponents.AnimationState], + spriteMatrixes *ecs.ComponentManager[stdcomponents.SpriteMatrix]) *AnimationSpriteMatrixSystem { + return &AnimationSpriteMatrixSystem{ + world: world, + animationPlayers: animationPlayers, + animationStates: animationStates, + spriteMatrixes: spriteMatrixes, + } +} + +type AnimationSpriteMatrixSystem struct { + world *ecs.World + animationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] + animationStates *ecs.ComponentManager[stdcomponents.AnimationState] + spriteMatrixes *ecs.ComponentManager[stdcomponents.SpriteMatrix] +} + +func (s *AnimationSpriteMatrixSystem) Init() {} +func (s *AnimationSpriteMatrixSystem) Run(dt time.Duration) { + s.animationPlayers.AllParallel(func(e ecs.Entity, animationPlayer *stdcomponents.AnimationPlayer) bool { + spriteMatrix := s.spriteMatrixes.Get(e) + if spriteMatrix == nil { + return true + } + + animationStatePtr := s.animationStates.Get(e) + if animationStatePtr == nil { + return true + } + animationState := *animationStatePtr + + if animationPlayer.State == animationState && animationPlayer.IsInitialized == true { + return true + } + + currentAnimation := spriteMatrix.Animations[animationState] + + animationPlayer.First = 0 + animationPlayer.Current = 0 + animationPlayer.Last = currentAnimation.NumOfFrames - 1 + animationPlayer.Loop = currentAnimation.Loop + animationPlayer.Vertical = currentAnimation.Vertical + animationPlayer.FrameDuration = time.Second / time.Duration(spriteMatrix.FPS) + animationPlayer.State = animationState + animationPlayer.Speed = 1 + animationPlayer.IsInitialized = true + + return true + }) +} +func (s *AnimationSpriteMatrixSystem) Destroy() {} diff --git a/stdsystems/asset-library.go b/stdsystems/asset-library.go new file mode 100644 index 00000000..825ac1b9 --- /dev/null +++ b/stdsystems/asset-library.go @@ -0,0 +1,34 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "time" +) + +func NewAssetLibSystem(assets []ecs.AnyAssetLibrary) *AssetLibSystem { + return &AssetLibSystem{ + assets: assets, + } +} + +type AssetLibSystem struct { + assets []ecs.AnyAssetLibrary +} + +func (s *AssetLibSystem) Init() {} +func (s *AssetLibSystem) Run(dt time.Duration) { + for _, asset := range s.assets { + asset.LoadAll() + } +} +func (s *AssetLibSystem) Destroy() { + for _, asset := range s.assets { + asset.UnloadAll() + } +} diff --git a/stdsystems/debug.go b/stdsystems/debug.go new file mode 100644 index 00000000..9c04145a --- /dev/null +++ b/stdsystems/debug.go @@ -0,0 +1,45 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "fmt" + "log" + "os" + "runtime/pprof" + "time" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +func NewDebugSystem() *DebugSystem { + return &DebugSystem{} +} + +type DebugSystem struct { + pprofEnabled bool +} + +func (s *DebugSystem) Init() {} +func (s *DebugSystem) Run(dt time.Duration) { + if rl.IsKeyPressed(rl.KeyF9) { + if s.pprofEnabled { + pprof.StopCPUProfile() + fmt.Println("CPU Profile Stopped") + } else { + f, err := os.Create("cpu.out") + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + fmt.Println("CPU Profile Started") + } + + s.pprofEnabled = !s.pprofEnabled + } +} +func (s *DebugSystem) Destroy() {} diff --git a/stdsystems/network-receive.go b/stdsystems/network-receive.go new file mode 100644 index 00000000..a3bf1342 --- /dev/null +++ b/stdsystems/network-receive.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import "time" + +func NewNetworkReceiveSystem() *NetworkReceiveSystem { + return &NetworkReceiveSystem{} +} + +type NetworkReceiveSystem struct{} + +func (s *NetworkReceiveSystem) Init() {} +func (s *NetworkReceiveSystem) Run(dt time.Duration) {} +func (s *NetworkReceiveSystem) Destroy() {} diff --git a/stdsystems/network-send.go b/stdsystems/network-send.go new file mode 100644 index 00000000..c522b519 --- /dev/null +++ b/stdsystems/network-send.go @@ -0,0 +1,86 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + "fmt" + "gomp/network" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewNetworkSendSystem(world *ecs.World, + positions *stdcomponents.PositionComponentManager, + rotations *stdcomponents.RotationComponentManager, + mirroreds *stdcomponents.FlipComponentManager, +) *NetworkSendSystem { + return &NetworkSendSystem{ + world: world, + positions: positions, + rotations: rotations, + mirroreds: mirroreds, + } +} + +type NetworkSendSystem struct { + world *ecs.World + positions *stdcomponents.PositionComponentManager + rotations *stdcomponents.RotationComponentManager + mirroreds *stdcomponents.FlipComponentManager +} + +func (s *NetworkSendSystem) Init() { + s.positions.TrackChanges = true + s.rotations.TrackChanges = true + s.mirroreds.TrackChanges = true + + s.positions.SetEncoder(func(components []stdcomponents.Position) []byte { + data := make([]byte, 0) + for _, component := range components { + binary := fmt.Sprintf("%b", component.X) + data = append(data, []byte(binary)...) + } + + return data + }) + s.rotations.SetEncoder(func(components []stdcomponents.Rotation) []byte { + data := make([]byte, 0) + for _, component := range components { + binary := fmt.Sprintf("%b", component.Angle) + data = append(data, []byte(binary)...) + } + + return data + }) + s.mirroreds.SetEncoder(func(components []stdcomponents.Flip) []byte { + data := make([]byte, 0) + for _, component := range components { + binary := fmt.Sprintf("%b", component.X) + data = append(data, []byte(binary)...) + } + + return data + }) +} +func (s *NetworkSendSystem) Run(dt time.Duration) { + //patch := world.PatchGet() + //world.PatchReset() + //log.Printf("%v", patch) + if network.Quic.Mode() != network.ModeNone { + network.Quic.Send([]byte("patch"), 0) + } +} +func (s *NetworkSendSystem) Destroy() {} diff --git a/stdsystems/network.go b/stdsystems/network.go new file mode 100644 index 00000000..070ab709 --- /dev/null +++ b/stdsystems/network.go @@ -0,0 +1,49 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdsystems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/network" + "time" +) + +type NetworkMode int + +const ( + None NetworkMode = iota + Server + Client +) + +func NewNetworkSystem() *NetworkSystem { + return &NetworkSystem{} +} + +type NetworkSystem struct { +} + +func (s *NetworkSystem) Init() { +} +func (s *NetworkSystem) Run(dt time.Duration) { + if rl.IsKeyPressed(rl.KeyP) { + network.Quic.Host("127.0.0.1:27015") + } + + if rl.IsKeyPressed(rl.KeyO) { + network.Quic.Connect("127.0.0.1:27015") + } +} +func (s *NetworkSystem) Destroy() {} diff --git a/stdsystems/render.go b/stdsystems/render.go new file mode 100644 index 00000000..6e919136 --- /dev/null +++ b/stdsystems/render.go @@ -0,0 +1,61 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "fmt" + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +type RenderSystem struct { + world *ecs.World + textureRenders *ecs.ComponentManager[stdcomponents.TextureRender] +} + +func NewRenderSystem(world *ecs.World, textureRenders *ecs.ComponentManager[stdcomponents.TextureRender]) *RenderSystem { + return &RenderSystem{ + world: world, + textureRenders: textureRenders, + } +} + +func (s *RenderSystem) Init() { + rl.InitWindow(800, 600, "raylib [core] ebiten-ecs - basic window") + currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) + rl.SetTargetFPS(currentMonitorRefreshRate) +} +func (s *RenderSystem) Run(dt time.Duration) { + if rl.WindowShouldClose() { + s.world.SetShouldDestroy(true) + return + } + + rl.BeginDrawing() + + rl.ClearBackground(rl.Black) + + s.textureRenders.AllData(func(tr *stdcomponents.TextureRender) bool { + texture := *tr.Texture + rl.DrawTexturePro(texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) + return true + }) + + // rl.DrawRectangle(0, 0, 120, 120, rl.DarkGray) + rl.DrawFPS(10, 10) + rl.DrawText(fmt.Sprintf("%d", s.world.Size()), 10, 30, 20, rl.Red) + rl.DrawText(fmt.Sprintf("%s", dt), 10, 50, 20, rl.Red) + rl.DrawText(fmt.Sprintf("%s", s.world.DtFixedUpdate()), 10, 70, 20, rl.Red) + + rl.EndDrawing() +} + +func (s *RenderSystem) Destroy() { + rl.CloseWindow() +} diff --git a/stdsystems/texture-render-animation.go b/stdsystems/texture-render-animation.go new file mode 100644 index 00000000..ea11dc53 --- /dev/null +++ b/stdsystems/texture-render-animation.go @@ -0,0 +1,54 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +// TextureRenderAnimationSystem is a system that sets Position of textureRender +type TextureRenderAnimationSystem struct { + animations *stdcomponents.AnimationPlayerComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager +} + +func (s *TextureRenderAnimationSystem) Init() {} +func (s *TextureRenderAnimationSystem) Run(dt time.Duration) { + // Run sprites and spriteRenders + s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + if tr == nil { + return true + } + + animation := s.animations.Get(entity) + if animation == nil { + return true + } + + frame := &tr.Frame + if animation.Vertical { + frame.Y += frame.Height * float32(animation.Current) + } else { + frame.X += frame.Width * float32(animation.Current) + } + + return true + }) +} +func (s *TextureRenderAnimationSystem) Destroy() {} + +func NewTextureRenderAnimationSystem( + animations *stdcomponents.AnimationPlayerComponentManager, + textureRenders *stdcomponents.TextureRenderComponentManager, +) *TextureRenderAnimationSystem { + return &TextureRenderAnimationSystem{ + animations: animations, + textureRenders: textureRenders, + } +} diff --git a/stdsystems/texture-render-flip.go b/stdsystems/texture-render-flip.go new file mode 100644 index 00000000..9b730cc6 --- /dev/null +++ b/stdsystems/texture-render-flip.go @@ -0,0 +1,54 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRenderFlipSystem( + flips *stdcomponents.FlipComponentManager, + textureRenders *stdcomponents.TextureRenderComponentManager, +) *TextureRenderFlipSystem { + return &TextureRenderFlipSystem{ + flips: flips, + textureRenders: textureRenders, + } +} + +// TextureRenderFlipSystem is a system that sets Scale of textureRender +type TextureRenderFlipSystem struct { + flips *stdcomponents.FlipComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager +} + +func (s *TextureRenderFlipSystem) Init() {} +func (s *TextureRenderFlipSystem) Run(dt time.Duration) { + // Run sprites and spriteRenders + s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + if tr == nil { + return true + } + + mirrored := s.flips.Get(entity) + if mirrored == nil { + return true + } + + if mirrored.X { + tr.Frame.Width *= -1 + } + if mirrored.Y { + tr.Frame.Height *= -1 + } + + return true + }) +} +func (s *TextureRenderFlipSystem) Destroy() {} diff --git a/stdsystems/texture-render-position.go b/stdsystems/texture-render-position.go new file mode 100644 index 00000000..f205d1ae --- /dev/null +++ b/stdsystems/texture-render-position.go @@ -0,0 +1,46 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRenderPositionSystem(position *stdcomponents.PositionComponentManager, render *ecs.ComponentManager[stdcomponents.TextureRender]) *TextureRenderPositionSystem { + return &TextureRenderPositionSystem{ + positions: position, + textureRenders: render, + } +} + +// TextureRenderPositionSystem is a system that sets Position of textureRender +type TextureRenderPositionSystem struct { + positions *stdcomponents.PositionComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager +} + +func (s *TextureRenderPositionSystem) Init() {} +func (s *TextureRenderPositionSystem) Run(dt time.Duration) { + s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + if tr == nil { + return true + } + + position := s.positions.Get(entity) + if position == nil { + return true + } + + tr.Dest.X = position.X + tr.Dest.Y = position.Y + + return true + }) +} +func (s *TextureRenderPositionSystem) Destroy() {} diff --git a/stdsystems/texture-render-rotation.go b/stdsystems/texture-render-rotation.go new file mode 100644 index 00000000..f43b18b3 --- /dev/null +++ b/stdsystems/texture-render-rotation.go @@ -0,0 +1,46 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRenderRotationSystem(rotation *ecs.ComponentManager[stdcomponents.Rotation], render *ecs.ComponentManager[stdcomponents.TextureRender]) *TextureRenderRotationSystem { + return &TextureRenderRotationSystem{ + rotations: rotation, + textureRenders: render, + } +} + +// TextureRenderRotationSystem is a system that sets Rotation of textureRender +type TextureRenderRotationSystem struct { + rotations *stdcomponents.RotationComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager +} + +func (s *TextureRenderRotationSystem) Init() {} +func (s *TextureRenderRotationSystem) Run(dt time.Duration) { + // Run sprites and spriteRenders + s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + if tr == nil { + return true + } + + rotation := s.rotations.Get(entity) + if rotation == nil { + return true + } + + tr.Rotation = rotation.Angle + + return true + }) +} +func (s *TextureRenderRotationSystem) Destroy() {} diff --git a/stdsystems/texture-render-scale.go b/stdsystems/texture-render-scale.go new file mode 100644 index 00000000..f4664db5 --- /dev/null +++ b/stdsystems/texture-render-scale.go @@ -0,0 +1,46 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRenderScaleSystem(scale *ecs.ComponentManager[stdcomponents.Scale], render *ecs.ComponentManager[stdcomponents.TextureRender]) *TextureRenderScaleSystem { + return &TextureRenderScaleSystem{ + scales: scale, + textureRenders: render, + } +} + +// TextureRenderScaleSystem is a system that sets Scale of textureRender +type TextureRenderScaleSystem struct { + scales *stdcomponents.ScaleComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager +} + +func (s *TextureRenderScaleSystem) Init() {} +func (s *TextureRenderScaleSystem) Run(dt time.Duration) { + s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + if tr == nil { + return true + } + + scale := s.scales.Get(entity) + if scale == nil { + return true + } + + tr.Dest.Width *= scale.X + tr.Dest.Height *= scale.Y + + return true + }) +} +func (s *TextureRenderScaleSystem) Destroy() {} diff --git a/stdsystems/texture-render-sprite.go b/stdsystems/texture-render-sprite.go new file mode 100644 index 00000000..d31fe400 --- /dev/null +++ b/stdsystems/texture-render-sprite.go @@ -0,0 +1,95 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +<- Монтажер сука Donated 50 RUB + +Thank you for your support! +*/ + +package stdsystems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRenderSpriteSystem( + sprites *stdcomponents.SpriteComponentManager, + textureRenders *stdcomponents.TextureRenderComponentManager, +) *TextureRenderSpriteSystem { + return &TextureRenderSpriteSystem{ + sprites: sprites, + textureRenders: textureRenders, + } +} + +// TextureRenderSpriteSystem is a system that prepares Sprite to be rendered +type TextureRenderSpriteSystem struct { + sprites *stdcomponents.SpriteComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager +} + +func (s *TextureRenderSpriteSystem) Init() {} +func (s *TextureRenderSpriteSystem) Run(dt time.Duration) { + // Run sprites and spriteRenders + s.sprites.AllParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { + if sprite == nil { + return true + } + + spriteFrame := sprite.Frame + spriteOrigin := sprite.Origin + spriteTint := sprite.Tint + + tr := s.textureRenders.Get(entity) + if tr == nil { + // Create new spriteRender + newRender := stdcomponents.TextureRender{ + Texture: sprite.Texture, + Frame: sprite.Frame, + Origin: sprite.Origin, + Tint: sprite.Tint, + Dest: rl.NewRectangle( + 0, + 0, + sprite.Frame.Width, + sprite.Frame.Height, + ), + } + + s.textureRenders.Create(entity, newRender) + } else { + // Run spriteRender + // tr.Texture = sprite.Texture + trFrame := &tr.Frame + trFrame.X = spriteFrame.X + trFrame.Y = spriteFrame.Y + trFrame.Width = spriteFrame.Width + trFrame.Height = spriteFrame.Height + + trOrigin := &tr.Origin + trOrigin.X = spriteOrigin.X + trOrigin.Y = spriteOrigin.Y + + trTint := &tr.Tint + trTint.A = spriteTint.A + trTint.R = spriteTint.R + trTint.G = spriteTint.G + trTint.B = spriteTint.B + + trDest := &tr.Dest + trDest.Width = spriteFrame.Width + trDest.Height = spriteFrame.Height + } + return true + }) +} +func (s *TextureRenderSpriteSystem) Destroy() {} diff --git a/stdsystems/texture-render-spritematrix.go b/stdsystems/texture-render-spritematrix.go new file mode 100644 index 00000000..9a53cfbd --- /dev/null +++ b/stdsystems/texture-render-spritematrix.go @@ -0,0 +1,77 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRenderMatrixSystem( + spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, + textureRenders *stdcomponents.TextureRenderComponentManager, + animationStates *stdcomponents.AnimationStateComponentManager, +) *TextureRenderMatrixSystem { + return &TextureRenderMatrixSystem{ + spriteMatrixes: spriteMatrixes, + textureRenders: textureRenders, + animationStates: animationStates, + } +} + +// TextureRenderMatrixSystem is a system that prepares SpriteSheet to be rendered +type TextureRenderMatrixSystem struct { + spriteMatrixes *stdcomponents.SpriteMatrixComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager + animationStates *stdcomponents.AnimationStateComponentManager +} + +func (s *TextureRenderMatrixSystem) Init() {} +func (s *TextureRenderMatrixSystem) Run(dt time.Duration) { + // Run sprites and spriteRenders + s.spriteMatrixes.AllParallel(func(entity ecs.Entity, spriteMatrix *stdcomponents.SpriteMatrix) bool { + if spriteMatrix == nil { + return true + } + + animationState := s.animationStates.Get(entity) + if animationState == nil { + return true + } + + currentAnimationFrame := spriteMatrix.Animations[*animationState].Frame + + tr := s.textureRenders.Get(entity) + if tr == nil { + // Create new spriteRender + newRender := stdcomponents.TextureRender{ + Texture: spriteMatrix.Texture, + Origin: spriteMatrix.Origin, + Frame: currentAnimationFrame, + Dest: rl.Rectangle{ + Width: currentAnimationFrame.Width, + Height: currentAnimationFrame.Height, + }, + } + + s.textureRenders.Create(entity, newRender) + } else { + // Run spriteRender + tr.Texture = spriteMatrix.Texture + tr.Origin = spriteMatrix.Origin + tr.Dest = rl.Rectangle{ + Width: currentAnimationFrame.Width, + Height: currentAnimationFrame.Height, + } + tr.Frame = currentAnimationFrame + } + return true + }) +} +func (s *TextureRenderMatrixSystem) Destroy() {} diff --git a/stdsystems/texture-render-spritesheet.go b/stdsystems/texture-render-spritesheet.go new file mode 100644 index 00000000..3cf5a5d8 --- /dev/null +++ b/stdsystems/texture-render-spritesheet.go @@ -0,0 +1,64 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + rl "github.com/gen2brain/raylib-go/raylib" + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRenderSpriteSheetSystem( + spriteSheets *stdcomponents.SpriteSheetComponentManager, + textureRenders *stdcomponents.TextureRenderComponentManager, +) *TextureRenderSpriteSheetSystem { + return &TextureRenderSpriteSheetSystem{ + spriteSheets: spriteSheets, + textureRenders: textureRenders, + } +} + +// TextureRenderSpriteSheetSystem is a system that prepares SpriteSheet to be rendered +type TextureRenderSpriteSheetSystem struct { + spriteSheets *stdcomponents.SpriteSheetComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager +} + +func (s *TextureRenderSpriteSheetSystem) Init() {} +func (s *TextureRenderSpriteSheetSystem) Run(dt time.Duration) { + s.spriteSheets.AllParallel(func(entity ecs.Entity, spriteSheet *stdcomponents.SpriteSheet) bool { + if spriteSheet == nil { + return true + } + + tr := s.textureRenders.Get(entity) + if tr == nil { + // Create new spriteRender + newRender := stdcomponents.TextureRender{ + Texture: spriteSheet.Texture, + Frame: spriteSheet.Frame, + Origin: spriteSheet.Origin, + Dest: rl.NewRectangle( + 0, + 0, + spriteSheet.Frame.Width, + spriteSheet.Frame.Height, + ), + } + + s.textureRenders.Create(entity, newRender) + } else { + // Run spriteRender + tr.Texture = spriteSheet.Texture + tr.Frame = spriteSheet.Frame + tr.Origin = spriteSheet.Origin + } + return true + }) +} +func (s *TextureRenderSpriteSheetSystem) Destroy() {} diff --git a/stdsystems/texture-render-tint.go b/stdsystems/texture-render-tint.go new file mode 100644 index 00000000..ba9a9dc6 --- /dev/null +++ b/stdsystems/texture-render-tint.go @@ -0,0 +1,49 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewTextureRenderTintSystem(tint *ecs.ComponentManager[stdcomponents.Tint], render *ecs.ComponentManager[stdcomponents.TextureRender]) *TextureRenderTintSystem { + return &TextureRenderTintSystem{ + tints: tint, + textureRenders: render, + } +} + +// TextureRenderTintSystem is a system that sets Scale of textureRender +type TextureRenderTintSystem struct { + tints *stdcomponents.TintComponentManager + textureRenders *stdcomponents.TextureRenderComponentManager +} + +func (s *TextureRenderTintSystem) Init() {} +func (s *TextureRenderTintSystem) Run(dt time.Duration) { + s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + if tr == nil { + return true + } + + tint := s.tints.Get(entity) + if tint == nil { + return true + } + + trTint := &tr.Tint + trTint.A = tint.A + trTint.R = tint.R + trTint.G = tint.G + trTint.B = tint.B + + return true + }) +} +func (s *TextureRenderTintSystem) Destroy() {} From 58118d5af9c479ea000f3ec806db70cc3f584275 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 16 Feb 2025 19:59:54 +0300 Subject: [PATCH 002/196] fmt new-api --- engine.go | 17 +++--- examples/new-api/systems/color.go | 44 --------------- examples/new-api/systems/player.go | 48 ++++++++-------- examples/new-api/systems/spawn.go | 89 ------------------------------ 4 files changed, 32 insertions(+), 166 deletions(-) delete mode 100644 examples/new-api/systems/color.go delete mode 100644 examples/new-api/systems/spawn.go diff --git a/engine.go b/engine.go index f8fec438..be71a5cc 100644 --- a/engine.go +++ b/engine.go @@ -19,21 +19,14 @@ import ( "time" ) -type EngineSystems interface { +type EngineSystemSet interface { Init() Update(dt time.Duration) FixedUpdate(dt time.Duration) Destroy() } -type Engine[C any, S EngineSystems] struct { - World *ecs.World - Components C - Systems S -} - -func NewEngine[C any, S EngineSystems](world *ecs.World, components C, systems S) *Engine[C, S] { - +func NewEngine[C any, S EngineSystemSet](world *ecs.World, components C, systems S) *Engine[C, S] { newGame := Engine[C, S]{ World: world, Components: components, @@ -43,6 +36,12 @@ func NewEngine[C any, S EngineSystems](world *ecs.World, components C, systems S return &newGame } +type Engine[C any, S EngineSystemSet] struct { + World *ecs.World + Components C + Systems S +} + func (g *Engine[C, S]) Run(tickrate uint) { duration := time.Second / time.Duration(tickrate) diff --git a/examples/new-api/systems/color.go b/examples/new-api/systems/color.go deleted file mode 100644 index 64b50561..00000000 --- a/examples/new-api/systems/color.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/components" - ecs2 "gomp/pkg/ecs" - "image/color" -) - -type colorController struct { - baseColor color.RGBA -} - -func (s *colorController) Init(world *ecs2.World) { - s.baseColor = color.RGBA{25, 220, 200, 255} -} -func (s *colorController) Update(world *ecs2.World) {} -func (s *colorController) FixedUpdate(world *ecs2.World) { - sprites := components.SpriteService.GetManager(world) - hps := components.HealthService.GetManager(world) - - sprites.AllParallel(func(entity ecs2.Entity, sprite *components.Sprite) bool { - hp := hps.Get(entity) - if hp == nil { - return true - } - - hpPercentage := float32(hp.Hp) / float32(hp.MaxHp) - - sprite.Tint = color.RGBA{ - uint8(hpPercentage * float32(s.baseColor.R)), - uint8(hpPercentage * float32(s.baseColor.G)), - uint8(hpPercentage * float32(s.baseColor.B)), - s.baseColor.A, - } - return true - }) -} -func (s *colorController) Destroy(world *ecs2.World) {} diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index f9ea9c98..c315c075 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -14,6 +14,30 @@ import ( "time" ) +func NewPlayerSystem( + world *ecs.World, + spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, + positions *stdcomponents.PositionComponentManager, + rotations *stdcomponents.RotationComponentManager, + scales *stdcomponents.ScaleComponentManager, + animationPlayers *stdcomponents.AnimationPlayerComponentManager, + animationStates *stdcomponents.AnimationStateComponentManager, + tints *stdcomponents.TintComponentManager, + flips *stdcomponents.FlipComponentManager, +) *PlayerSystem { + return &PlayerSystem{ + world: world, + spriteMatrixes: spriteMatrixes, + positions: positions, + rotations: rotations, + scales: scales, + animationPlayers: animationPlayers, + animationStates: animationStates, + tints: tints, + flips: flips, + } +} + type PlayerSystem struct { world *ecs.World player entities.Player @@ -60,27 +84,3 @@ func (s *PlayerSystem) Run(dt time.Duration) { } } func (s *PlayerSystem) Destroy() {} - -func NewPlayerSystem( - world *ecs.World, - spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, - positions *stdcomponents.PositionComponentManager, - rotations *stdcomponents.RotationComponentManager, - scales *stdcomponents.ScaleComponentManager, - animationPlayers *stdcomponents.AnimationPlayerComponentManager, - animationStates *stdcomponents.AnimationStateComponentManager, - tints *stdcomponents.TintComponentManager, - flips *stdcomponents.FlipComponentManager, -) *PlayerSystem { - return &PlayerSystem{ - world: world, - spriteMatrixes: spriteMatrixes, - positions: positions, - rotations: rotations, - scales: scales, - animationPlayers: animationPlayers, - animationStates: animationStates, - tints: tints, - flips: flips, - } -} diff --git a/examples/new-api/systems/spawn.go b/examples/new-api/systems/spawn.go deleted file mode 100644 index 304dc6c2..00000000 --- a/examples/new-api/systems/spawn.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package systems - -import ( - "gomp/examples/raylib-ecs/assets" - "gomp/examples/raylib-ecs/components" - "gomp/pkg/ecs" - "math/rand" - - rl "github.com/gen2brain/raylib-go/raylib" -) - -type spawnController struct { - pprofEnabled bool -} - -const ( - minHpPercentage = 20 - minMaxHp = 500 - maxMaxHp = 2000 -) - -func (s *spawnController) Init(world *ecs.World) {} -func (s *spawnController) Update(world *ecs.World) { - sprites := components.SpriteService.GetManager(world) - healths := components.HealthService.GetManager(world) - positions := components.PositionService.GetManager(world) - rotations := components.RotationService.GetManager(world) - scales := components.ScaleService.GetManager(world) - - if rl.IsKeyDown(rl.KeySpace) { - for range rand.Intn(10000) { - if world.Size() > 100_000_000 { - break - } - - newCreature := world.CreateEntity("Creature") - - // Adding position component - t := components.Position{ - X: float32(rand.Int31n(800)), - Y: float32(rand.Int31n(600)), - } - positions.Create(newCreature, t) - - // Adding rotation component - rotation := components.Rotation{ - Angle: float32(rand.Int31n(360)), - } - rotations.Create(newCreature, rotation) - - // Adding scale component - scale := components.Scale{ - X: 2, - Y: 2, - } - scales.Create(newCreature, scale) - - // Adding HP component - maxHp := minMaxHp + rand.Int31n(maxMaxHp-minMaxHp) - hp := int32(float32(maxHp) * float32(minHpPercentage+rand.Int31n(100-minHpPercentage)) / 100) - h := components.Health{ - Hp: hp, - MaxHp: maxHp, - } - healths.Create(newCreature, h) - - texture := assets.Textures.Get("assets/star.png") - - // Adding sprite component - c := components.Sprite{ - Origin: rl.Vector2{X: 0.5, Y: 0.5}, - Texture: texture, - Frame: rl.Rectangle{X: 0, Y: 0, Width: float32(texture.Width), Height: float32(texture.Height)}, - } - - sprites.Create(newCreature, c) - } - } -} -func (s *spawnController) FixedUpdate(world *ecs.World) { -} - -func (s *spawnController) Destroy(world *ecs.World) {} From 126c085536f2a2a2ac3c8afb8bd2c7269b95480d Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 16 Feb 2025 20:11:26 +0300 Subject: [PATCH 003/196] fix types --- examples/new-api/desktop-components.go | 22 +++++++++++----------- stdcomponents/animation-player.go | 2 +- stdcomponents/animation-state.go | 2 +- stdcomponents/components.go | 5 ----- stdcomponents/flip.go | 2 +- stdcomponents/network.go | 4 +++- stdcomponents/position.go | 2 +- stdcomponents/rotation.go | 2 +- stdcomponents/scale.go | 2 +- stdcomponents/sprite-matrix.go | 2 +- stdcomponents/sprite-sheet.go | 2 +- stdcomponents/sprite.go | 2 +- stdcomponents/texture-render.go | 2 +- stdcomponents/tint.go | 2 +- 14 files changed, 25 insertions(+), 28 deletions(-) diff --git a/examples/new-api/desktop-components.go b/examples/new-api/desktop-components.go index 2abb44e9..7c6f6530 100644 --- a/examples/new-api/desktop-components.go +++ b/examples/new-api/desktop-components.go @@ -38,15 +38,15 @@ func NewDesktopComponents(world *ecs.World) *desktopComponents { type desktopComponents struct { position *stdcomponents.PositionComponentManager - rotation *ecs.ComponentManager[stdcomponents.Rotation] - scale *ecs.ComponentManager[stdcomponents.Scale] - flip *ecs.ComponentManager[stdcomponents.Flip] - sprite *ecs.ComponentManager[stdcomponents.Sprite] - spriteSheet *ecs.ComponentManager[stdcomponents.SpriteSheet] - spriteMatrix *ecs.ComponentManager[stdcomponents.SpriteMatrix] - tint *ecs.ComponentManager[stdcomponents.Tint] - animationPlayer *ecs.ComponentManager[stdcomponents.AnimationPlayer] - animationState *ecs.ComponentManager[stdcomponents.AnimationState] - textureRender *ecs.ComponentManager[stdcomponents.TextureRender] - network *ecs.ComponentManager[stdcomponents.Network] + rotation *stdcomponents.RotationComponentManager + scale *stdcomponents.ScaleComponentManager + flip *stdcomponents.FlipComponentManager + sprite *stdcomponents.SpriteComponentManager + spriteSheet *stdcomponents.SpriteSheetComponentManager + spriteMatrix *stdcomponents.SpriteMatrixComponentManager + tint *stdcomponents.TintComponentManager + animationPlayer *stdcomponents.AnimationPlayerComponentManager + animationState *stdcomponents.AnimationStateComponentManager + textureRender *stdcomponents.TextureRenderComponentManager + network *stdcomponents.NetworkComponentManager } diff --git a/stdcomponents/animation-player.go b/stdcomponents/animation-player.go index 06235809..78017541 100644 --- a/stdcomponents/animation-player.go +++ b/stdcomponents/animation-player.go @@ -34,6 +34,6 @@ type AnimationPlayer struct { type AnimationPlayerComponentManager = ecs.ComponentManager[AnimationPlayer] -func NewAnimationPlayerComponentManager(world *ecs.World) *ecs.ComponentManager[AnimationPlayer] { +func NewAnimationPlayerComponentManager(world *ecs.World) *AnimationPlayerComponentManager { return ecs.NewComponentManager[AnimationPlayer](world, ANIMATION_PLAYER_ID) } diff --git a/stdcomponents/animation-state.go b/stdcomponents/animation-state.go index 2b5c27bb..d0006ac4 100644 --- a/stdcomponents/animation-state.go +++ b/stdcomponents/animation-state.go @@ -22,6 +22,6 @@ type AnimationState int32 type AnimationStateComponentManager = ecs.ComponentManager[AnimationState] -func NewAnimationStateComponentManager(world *ecs.World) *ecs.ComponentManager[AnimationState] { +func NewAnimationStateComponentManager(world *ecs.World) *AnimationStateComponentManager { return ecs.NewComponentManager[AnimationState](world, ANIMATION_STATE_ID) } diff --git a/stdcomponents/components.go b/stdcomponents/components.go index 83247867..252fd87c 100644 --- a/stdcomponents/components.go +++ b/stdcomponents/components.go @@ -12,21 +12,16 @@ import ( const ( INVALID_ID ecs.ComponentID = iota + 128 - COLOR_ID POSITION_ID ROTATION_ID SCALE_ID FLIP_ID - HEALTH_ID - DESTROY_ID SPRITE_ID SPRITE_SHEET_ID SPRITE_MATRIX_ID TEXTURE_RENDER_ID - ANIMATION_ID ANIMATION_PLAYER_ID ANIMATION_STATE_ID TINT_ID - SPRITE_ANIMATOR_ID NETWORK_ID ) diff --git a/stdcomponents/flip.go b/stdcomponents/flip.go index 655f9cb0..4ab2d16e 100644 --- a/stdcomponents/flip.go +++ b/stdcomponents/flip.go @@ -22,6 +22,6 @@ type Flip struct { type FlipComponentManager = ecs.ComponentManager[Flip] -func NewFlipComponentManager(world *ecs.World) *ecs.ComponentManager[Flip] { +func NewFlipComponentManager(world *ecs.World) *FlipComponentManager { return ecs.NewComponentManager[Flip](world, FLIP_ID) } diff --git a/stdcomponents/network.go b/stdcomponents/network.go index 78d8452f..d16e531f 100644 --- a/stdcomponents/network.go +++ b/stdcomponents/network.go @@ -23,6 +23,8 @@ type Network struct { PatchOut []byte } -func NewNetworkComponentManager(world *ecs.World) *ecs.ComponentManager[Network] { +type NetworkComponentManager = ecs.ComponentManager[Network] + +func NewNetworkComponentManager(world *ecs.World) *NetworkComponentManager { return ecs.NewComponentManager[Network](world, NETWORK_ID) } diff --git a/stdcomponents/position.go b/stdcomponents/position.go index 6c3f279a..c519e05a 100644 --- a/stdcomponents/position.go +++ b/stdcomponents/position.go @@ -22,6 +22,6 @@ type Position struct { type PositionComponentManager = ecs.ComponentManager[Position] -func NewPositionComponentManager(world *ecs.World) *ecs.ComponentManager[Position] { +func NewPositionComponentManager(world *ecs.World) *PositionComponentManager { return ecs.NewComponentManager[Position](world, POSITION_ID) } diff --git a/stdcomponents/rotation.go b/stdcomponents/rotation.go index 11af4f3f..e3447013 100644 --- a/stdcomponents/rotation.go +++ b/stdcomponents/rotation.go @@ -22,6 +22,6 @@ type Rotation struct { type RotationComponentManager = ecs.ComponentManager[Rotation] -func NewRotationComponentManager(world *ecs.World) *ecs.ComponentManager[Rotation] { +func NewRotationComponentManager(world *ecs.World) *RotationComponentManager { return ecs.NewComponentManager[Rotation](world, ROTATION_ID) } diff --git a/stdcomponents/scale.go b/stdcomponents/scale.go index 9a4d96c9..d4efafeb 100644 --- a/stdcomponents/scale.go +++ b/stdcomponents/scale.go @@ -22,6 +22,6 @@ type Scale struct { type ScaleComponentManager = ecs.ComponentManager[Scale] -func NewScaleComponentManager(world *ecs.World) *ecs.ComponentManager[Scale] { +func NewScaleComponentManager(world *ecs.World) *ScaleComponentManager { return ecs.NewComponentManager[Scale](world, SCALE_ID) } diff --git a/stdcomponents/sprite-matrix.go b/stdcomponents/sprite-matrix.go index e6c0bb42..808d9b10 100644 --- a/stdcomponents/sprite-matrix.go +++ b/stdcomponents/sprite-matrix.go @@ -36,6 +36,6 @@ type SpriteMatrix struct { type SpriteMatrixComponentManager = ecs.ComponentManager[SpriteMatrix] -func NewSpriteMatrixComponentManager(world *ecs.World) *ecs.ComponentManager[SpriteMatrix] { +func NewSpriteMatrixComponentManager(world *ecs.World) *SpriteMatrixComponentManager { return ecs.NewComponentManager[SpriteMatrix](world, SPRITE_MATRIX_ID) } diff --git a/stdcomponents/sprite-sheet.go b/stdcomponents/sprite-sheet.go index 98c64d81..c9e4eb64 100644 --- a/stdcomponents/sprite-sheet.go +++ b/stdcomponents/sprite-sheet.go @@ -30,6 +30,6 @@ type SpriteSheet struct { type SpriteSheetComponentManager = ecs.ComponentManager[SpriteSheet] -func NewSpriteSheetComponentManager(world *ecs.World) *ecs.ComponentManager[SpriteSheet] { +func NewSpriteSheetComponentManager(world *ecs.World) *SpriteSheetComponentManager { return ecs.NewComponentManager[SpriteSheet](world, SPRITE_SHEET_ID) } diff --git a/stdcomponents/sprite.go b/stdcomponents/sprite.go index 197d9ce5..6d43f3c2 100644 --- a/stdcomponents/sprite.go +++ b/stdcomponents/sprite.go @@ -29,6 +29,6 @@ type Sprite struct { type SpriteComponentManager = ecs.ComponentManager[Sprite] -func NewSpriteComponentManager(world *ecs.World) *ecs.ComponentManager[Sprite] { +func NewSpriteComponentManager(world *ecs.World) *SpriteComponentManager { return ecs.NewComponentManager[Sprite](world, SPRITE_ID) } diff --git a/stdcomponents/texture-render.go b/stdcomponents/texture-render.go index 81e24dc5..34d69d00 100644 --- a/stdcomponents/texture-render.go +++ b/stdcomponents/texture-render.go @@ -31,6 +31,6 @@ type TextureRender struct { type TextureRenderComponentManager = ecs.ComponentManager[TextureRender] -func NewTextureRenderComponentManager(world *ecs.World) *ecs.ComponentManager[TextureRender] { +func NewTextureRenderComponentManager(world *ecs.World) *TextureRenderComponentManager { return ecs.NewComponentManager[TextureRender](world, TEXTURE_RENDER_ID) } diff --git a/stdcomponents/tint.go b/stdcomponents/tint.go index a15dd9e1..6d4349ca 100644 --- a/stdcomponents/tint.go +++ b/stdcomponents/tint.go @@ -23,6 +23,6 @@ type Tint = color.RGBA type TintComponentManager = ecs.ComponentManager[Tint] -func NewTintComponentManager(world *ecs.World) *ecs.ComponentManager[Tint] { +func NewTintComponentManager(world *ecs.World) *TintComponentManager { return ecs.NewComponentManager[Tint](world, TINT_ID) } From fe7fcc1ec796ddeaca00fe004811f761f18ec6a9 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Sun, 16 Feb 2025 21:46:41 +0300 Subject: [PATCH 004/196] refactors --- desktop-components.go | 52 ++++++ desktop-systems.go | 199 ++++++++++++++++++++++ examples/new-api/desktop.go => desktop.go | 12 +- engine.go | 6 +- examples/new-api/desktop-components.go | 52 ------ examples/new-api/desktop-systems.go | 186 -------------------- examples/new-api/game.go | 22 +++ examples/new-api/scenes/main.go | 47 +++++ scene.go | 38 +++++ 9 files changed, 367 insertions(+), 247 deletions(-) create mode 100644 desktop-components.go create mode 100644 desktop-systems.go rename examples/new-api/desktop.go => desktop.go (67%) delete mode 100644 examples/new-api/desktop-components.go delete mode 100644 examples/new-api/desktop-systems.go create mode 100644 examples/new-api/game.go create mode 100644 examples/new-api/scenes/main.go create mode 100644 scene.go diff --git a/desktop-components.go b/desktop-components.go new file mode 100644 index 00000000..c6b93711 --- /dev/null +++ b/desktop-components.go @@ -0,0 +1,52 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +func NewDesktopComponents(world *ecs.World) *DesktopComponents { + return &DesktopComponents{ + Position: stdcomponents.NewPositionComponentManager(world), + Rotation: stdcomponents.NewRotationComponentManager(world), + Scale: stdcomponents.NewScaleComponentManager(world), + Flip: stdcomponents.NewFlipComponentManager(world), + Sprite: stdcomponents.NewSpriteComponentManager(world), + SpriteSheet: stdcomponents.NewSpriteSheetComponentManager(world), + SpriteMatrix: stdcomponents.NewSpriteMatrixComponentManager(world), + Tint: stdcomponents.NewTintComponentManager(world), + AnimationPlayer: stdcomponents.NewAnimationPlayerComponentManager(world), + AnimationState: stdcomponents.NewAnimationStateComponentManager(world), + TextureRender: stdcomponents.NewTextureRenderComponentManager(world), + Network: stdcomponents.NewNetworkComponentManager(world), + } +} + +type DesktopComponents struct { + Position *stdcomponents.PositionComponentManager + Rotation *stdcomponents.RotationComponentManager + Scale *stdcomponents.ScaleComponentManager + Flip *stdcomponents.FlipComponentManager + Sprite *stdcomponents.SpriteComponentManager + SpriteSheet *stdcomponents.SpriteSheetComponentManager + SpriteMatrix *stdcomponents.SpriteMatrixComponentManager + Tint *stdcomponents.TintComponentManager + AnimationPlayer *stdcomponents.AnimationPlayerComponentManager + AnimationState *stdcomponents.AnimationStateComponentManager + TextureRender *stdcomponents.TextureRenderComponentManager + Network *stdcomponents.NetworkComponentManager +} diff --git a/desktop-systems.go b/desktop-systems.go new file mode 100644 index 00000000..4abd59e8 --- /dev/null +++ b/desktop-systems.go @@ -0,0 +1,199 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import ( + "gomp/examples/new-api/assets" + "gomp/pkg/ecs" + "gomp/stdsystems" + "time" +) + +func NewDesktopSystems(world *ecs.World, components *DesktopComponents, scenes []Scene) *DesktopSystems { + return &DesktopSystems{ + Scenes: scenes, + Debug: stdsystems.NewDebugSystem(), + + Network: stdsystems.NewNetworkSystem(), + NetworkReceive: stdsystems.NewNetworkReceiveSystem(), + NetworkSend: stdsystems.NewNetworkSendSystem(world, components.Position, components.Rotation, components.Flip), + + AnimationSpriteMatrix: stdsystems.NewAnimationSpriteMatrixSystem(world, components.AnimationPlayer, components.AnimationState, components.SpriteMatrix), + AnimationPlayer: stdsystems.NewAnimationPlayerSystem(components.AnimationPlayer), + + TextureRenderSprite: stdsystems.NewTextureRenderSpriteSystem(components.Sprite, components.TextureRender), + TextureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(components.SpriteSheet, components.TextureRender), + TextureRenderMatrix: stdsystems.NewTextureRenderMatrixSystem(components.SpriteMatrix, components.TextureRender, components.AnimationState), + + TextureRenderAnimation: stdsystems.NewTextureRenderAnimationSystem(components.AnimationPlayer, components.TextureRender), + TextureRenderFlip: stdsystems.NewTextureRenderFlipSystem(components.Flip, components.TextureRender), + TextureRenderPosition: stdsystems.NewTextureRenderPositionSystem(components.Position, components.TextureRender), + TextureRenderRotation: stdsystems.NewTextureRenderRotationSystem(components.Rotation, components.TextureRender), + TextureRenderScale: stdsystems.NewTextureRenderScaleSystem(components.Scale, components.TextureRender), + TextureRenderTint: stdsystems.NewTextureRenderTintSystem(components.Tint, components.TextureRender), + + AssetLib: stdsystems.NewAssetLibSystem([]ecs.AnyAssetLibrary{assets.Textures}), + Render: stdsystems.NewRenderSystem(world, components.TextureRender), + } +} + +type DesktopSystems struct { + Scenes []Scene + Debug *stdsystems.DebugSystem + // Network + Network *stdsystems.NetworkSystem + NetworkReceive *stdsystems.NetworkReceiveSystem + NetworkSend *stdsystems.NetworkSendSystem + // Animation + AnimationSpriteMatrix *stdsystems.AnimationSpriteMatrixSystem + AnimationPlayer *stdsystems.AnimationPlayerSystem + // Prerender init + TextureRenderSprite *stdsystems.TextureRenderSpriteSystem + TextureRenderSpriteSheet *stdsystems.TextureRenderSpriteSheetSystem + TextureRenderMatrix *stdsystems.TextureRenderMatrixSystem + // Prerender fill + TextureRenderAnimation *stdsystems.TextureRenderAnimationSystem + TextureRenderFlip *stdsystems.TextureRenderFlipSystem + TextureRenderPosition *stdsystems.TextureRenderPositionSystem + TextureRenderRotation *stdsystems.TextureRenderRotationSystem + TextureRenderScale *stdsystems.TextureRenderScaleSystem + TextureRenderTint *stdsystems.TextureRenderTintSystem + // Render + AssetLib *stdsystems.AssetLibSystem + Render *stdsystems.RenderSystem +} + +func (s *DesktopSystems) Init() { + // Network receive + s.Network.Init() + s.NetworkReceive.Init() + + // Network patches + s.NetworkSend.Init() + + // Scenes + for i := range s.Scenes { + if s.Scenes[i].Enabled { + s.Scenes[i].Init() + } + } + + // Animation + s.AnimationSpriteMatrix.Init() + s.AnimationPlayer.Init() + + // Prerender init + s.TextureRenderSprite.Init() + s.TextureRenderSpriteSheet.Init() + s.TextureRenderMatrix.Init() + + // Prerender fill + s.TextureRenderAnimation.Init() + s.TextureRenderFlip.Init() + s.TextureRenderPosition.Init() + s.TextureRenderRotation.Init() + s.TextureRenderScale.Init() + s.TextureRenderTint.Init() + + // Render + s.Render.Init() + s.Debug.Init() + s.AssetLib.Init() +} + +func (s *DesktopSystems) Update(dt time.Duration) { + // Network receive + s.Network.Run(dt) + s.NetworkReceive.Run(dt) + + // Scenes + for i := range s.Scenes { + if s.Scenes[i].Enabled { + s.Scenes[i].Update(dt) + } + } + + // Animation + s.AnimationSpriteMatrix.Run(dt) + s.AnimationPlayer.Run(dt) + + // Prerender init + s.TextureRenderSprite.Run(dt) + s.TextureRenderSpriteSheet.Run(dt) + s.TextureRenderMatrix.Run(dt) + + // Prerender fill + s.TextureRenderAnimation.Run(dt) + s.TextureRenderFlip.Run(dt) + s.TextureRenderPosition.Run(dt) + s.TextureRenderRotation.Run(dt) + s.TextureRenderScale.Run(dt) + s.TextureRenderTint.Run(dt) + + // Render + s.AssetLib.Run(dt) + s.Debug.Run(dt) + s.Render.Run(dt) +} + +func (s *DesktopSystems) FixedUpdate(dt time.Duration) { + // Scenes + for i := range s.Scenes { + if s.Scenes[i].Enabled { + s.Scenes[i].FixedUpdate(dt) + } + } + + // Network send + s.NetworkSend.Run(dt) +} + +func (s *DesktopSystems) Destroy() { + // Network intents + s.Network.Destroy() + s.NetworkReceive.Destroy() + + // Scenes + for i := range s.Scenes { + if s.Scenes[i].IsInitialized { + s.Scenes[i].Destroy() + } + } + + // Network patches + s.NetworkSend.Destroy() + + // Animation + s.AnimationSpriteMatrix.Destroy() + s.AnimationPlayer.Destroy() + + // Prerender init + s.TextureRenderSprite.Destroy() + s.TextureRenderSpriteSheet.Destroy() + s.TextureRenderMatrix.Destroy() + + // Prerender fill + s.TextureRenderAnimation.Destroy() + s.TextureRenderFlip.Destroy() + s.TextureRenderPosition.Destroy() + s.TextureRenderRotation.Destroy() + s.TextureRenderScale.Destroy() + s.TextureRenderTint.Destroy() + + // Render + s.Debug.Destroy() + s.AssetLib.Destroy() + s.Render.Destroy() +} diff --git a/examples/new-api/desktop.go b/desktop.go similarity index 67% rename from examples/new-api/desktop.go rename to desktop.go index cb20b91c..c3c6ef3d 100644 --- a/examples/new-api/desktop.go +++ b/desktop.go @@ -12,20 +12,20 @@ none :) Thank you for your support! */ -package main +package gomp import ( - "gomp" "gomp/pkg/ecs" ) -func main() { +type DesktopEngine = Engine[*DesktopComponents, *DesktopSystems] + +func NewDesktopEngine(scenes []Scene) *DesktopEngine { world := ecs.CreateWorld("main") defer world.Destroy() components := NewDesktopComponents(world) - systems := NewDesktopSystems(world, components) + systems := NewDesktopSystems(world, components, scenes) - engine := gomp.NewEngine(world, components, systems) - engine.Run(60) + return NewEngine(world, components, systems) } diff --git a/engine.go b/engine.go index be71a5cc..6584a393 100644 --- a/engine.go +++ b/engine.go @@ -19,14 +19,14 @@ import ( "time" ) -type EngineSystemSet interface { +type AnyEngineSystemSet interface { Init() Update(dt time.Duration) FixedUpdate(dt time.Duration) Destroy() } -func NewEngine[C any, S EngineSystemSet](world *ecs.World, components C, systems S) *Engine[C, S] { +func NewEngine[C any, S AnyEngineSystemSet](world *ecs.World, components C, systems S) *Engine[C, S] { newGame := Engine[C, S]{ World: world, Components: components, @@ -36,7 +36,7 @@ func NewEngine[C any, S EngineSystemSet](world *ecs.World, components C, systems return &newGame } -type Engine[C any, S EngineSystemSet] struct { +type Engine[C any, S AnyEngineSystemSet] struct { World *ecs.World Components C Systems S diff --git a/examples/new-api/desktop-components.go b/examples/new-api/desktop-components.go deleted file mode 100644 index 7c6f6530..00000000 --- a/examples/new-api/desktop-components.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package main - -import ( - "gomp/pkg/ecs" - "gomp/stdcomponents" -) - -func NewDesktopComponents(world *ecs.World) *desktopComponents { - return &desktopComponents{ - position: stdcomponents.NewPositionComponentManager(world), - rotation: stdcomponents.NewRotationComponentManager(world), - scale: stdcomponents.NewScaleComponentManager(world), - flip: stdcomponents.NewFlipComponentManager(world), - sprite: stdcomponents.NewSpriteComponentManager(world), - spriteSheet: stdcomponents.NewSpriteSheetComponentManager(world), - spriteMatrix: stdcomponents.NewSpriteMatrixComponentManager(world), - tint: stdcomponents.NewTintComponentManager(world), - animationPlayer: stdcomponents.NewAnimationPlayerComponentManager(world), - animationState: stdcomponents.NewAnimationStateComponentManager(world), - textureRender: stdcomponents.NewTextureRenderComponentManager(world), - network: stdcomponents.NewNetworkComponentManager(world), - } -} - -type desktopComponents struct { - position *stdcomponents.PositionComponentManager - rotation *stdcomponents.RotationComponentManager - scale *stdcomponents.ScaleComponentManager - flip *stdcomponents.FlipComponentManager - sprite *stdcomponents.SpriteComponentManager - spriteSheet *stdcomponents.SpriteSheetComponentManager - spriteMatrix *stdcomponents.SpriteMatrixComponentManager - tint *stdcomponents.TintComponentManager - animationPlayer *stdcomponents.AnimationPlayerComponentManager - animationState *stdcomponents.AnimationStateComponentManager - textureRender *stdcomponents.TextureRenderComponentManager - network *stdcomponents.NetworkComponentManager -} diff --git a/examples/new-api/desktop-systems.go b/examples/new-api/desktop-systems.go deleted file mode 100644 index 15825bec..00000000 --- a/examples/new-api/desktop-systems.go +++ /dev/null @@ -1,186 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package main - -import ( - "gomp/examples/new-api/assets" - "gomp/examples/new-api/systems" - "gomp/pkg/ecs" - "gomp/stdsystems" - "time" -) - -func NewDesktopSystems(world *ecs.World, components *desktopComponents) *desktopSystems { - return &desktopSystems{ - player: systems.NewPlayerSystem(world, components.spriteMatrix, components.position, components.rotation, components.scale, components.animationPlayer, components.animationState, components.tint, components.flip), - - debug: stdsystems.NewDebugSystem(), - - network: stdsystems.NewNetworkSystem(), - networkReceive: stdsystems.NewNetworkReceiveSystem(), - networkSend: stdsystems.NewNetworkSendSystem(world, components.position, components.rotation, components.flip), - - animationSpriteMatrix: stdsystems.NewAnimationSpriteMatrixSystem(world, components.animationPlayer, components.animationState, components.spriteMatrix), - animationPlayer: stdsystems.NewAnimationPlayerSystem(components.animationPlayer), - - textureRenderSprite: stdsystems.NewTextureRenderSpriteSystem(components.sprite, components.textureRender), - textureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(components.spriteSheet, components.textureRender), - textureRenderMatrix: stdsystems.NewTextureRenderMatrixSystem(components.spriteMatrix, components.textureRender, components.animationState), - - textureRenderAnimation: stdsystems.NewTextureRenderAnimationSystem(components.animationPlayer, components.textureRender), - textureRenderFlip: stdsystems.NewTextureRenderFlipSystem(components.flip, components.textureRender), - textureRenderPosition: stdsystems.NewTextureRenderPositionSystem(components.position, components.textureRender), - textureRenderRotation: stdsystems.NewTextureRenderRotationSystem(components.rotation, components.textureRender), - textureRenderScale: stdsystems.NewTextureRenderScaleSystem(components.scale, components.textureRender), - textureRenderTint: stdsystems.NewTextureRenderTintSystem(components.tint, components.textureRender), - - assetLib: stdsystems.NewAssetLibSystem([]ecs.AnyAssetLibrary{assets.Textures}), - render: stdsystems.NewRenderSystem(world, components.textureRender), - } -} - -type desktopSystems struct { - player *systems.PlayerSystem - - debug *stdsystems.DebugSystem - // Network - network *stdsystems.NetworkSystem - networkReceive *stdsystems.NetworkReceiveSystem - networkSend *stdsystems.NetworkSendSystem - // Animation - animationSpriteMatrix *stdsystems.AnimationSpriteMatrixSystem - animationPlayer *stdsystems.AnimationPlayerSystem - // Prerender init - textureRenderSprite *stdsystems.TextureRenderSpriteSystem - textureRenderSpriteSheet *stdsystems.TextureRenderSpriteSheetSystem - textureRenderMatrix *stdsystems.TextureRenderMatrixSystem - // Prerender fill - textureRenderAnimation *stdsystems.TextureRenderAnimationSystem - textureRenderFlip *stdsystems.TextureRenderFlipSystem - textureRenderPosition *stdsystems.TextureRenderPositionSystem - textureRenderRotation *stdsystems.TextureRenderRotationSystem - textureRenderScale *stdsystems.TextureRenderScaleSystem - textureRenderTint *stdsystems.TextureRenderTintSystem - // Render - assetLib *stdsystems.AssetLibSystem - render *stdsystems.RenderSystem -} - -func (s *desktopSystems) Init() { - // Network receive - s.network.Init() - s.networkReceive.Init() - - // Network patches - s.networkSend.Init() - - // Scenes - s.player.Init() - - // Animation - s.animationSpriteMatrix.Init() - s.animationPlayer.Init() - - // Prerender init - s.textureRenderSprite.Init() - s.textureRenderSpriteSheet.Init() - s.textureRenderMatrix.Init() - - // Prerender fill - s.textureRenderAnimation.Init() - s.textureRenderFlip.Init() - s.textureRenderPosition.Init() - s.textureRenderRotation.Init() - s.textureRenderScale.Init() - s.textureRenderTint.Init() - - // Render - s.render.Init() - s.debug.Init() - s.assetLib.Init() -} - -func (s *desktopSystems) Update(dt time.Duration) { - // Network receive - s.network.Run(dt) - s.networkReceive.Run(dt) - - // Scenes - s.player.Run(dt) - - // Animation - s.animationSpriteMatrix.Run(dt) - s.animationPlayer.Run(dt) - - // Prerender init - s.textureRenderSprite.Run(dt) - s.textureRenderSpriteSheet.Run(dt) - s.textureRenderMatrix.Run(dt) - - // Prerender fill - s.textureRenderAnimation.Run(dt) - s.textureRenderFlip.Run(dt) - s.textureRenderPosition.Run(dt) - s.textureRenderRotation.Run(dt) - s.textureRenderScale.Run(dt) - s.textureRenderTint.Run(dt) - - // Render - s.assetLib.Run(dt) - s.debug.Run(dt) - s.render.Run(dt) -} - -func (s *desktopSystems) FixedUpdate(dt time.Duration) { - // Scenes - s.player.Run(dt) - - // Network send - s.networkSend.Run(dt) -} - -func (s *desktopSystems) Destroy() { - // Network intents - s.network.Destroy() - s.networkReceive.Destroy() - - // Scenes - s.player.Destroy() - - // Network patches - s.networkSend.Destroy() - - // Animation - s.animationSpriteMatrix.Destroy() - s.animationPlayer.Destroy() - - // Prerender init - s.textureRenderSprite.Destroy() - s.textureRenderSpriteSheet.Destroy() - s.textureRenderMatrix.Destroy() - - // Prerender fill - s.textureRenderAnimation.Destroy() - s.textureRenderFlip.Destroy() - s.textureRenderPosition.Destroy() - s.textureRenderRotation.Destroy() - s.textureRenderScale.Destroy() - s.textureRenderTint.Destroy() - - // Render - s.debug.Destroy() - s.assetLib.Destroy() - s.render.Destroy() -} diff --git a/examples/new-api/game.go b/examples/new-api/game.go new file mode 100644 index 00000000..5343e407 --- /dev/null +++ b/examples/new-api/game.go @@ -0,0 +1,22 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package main + +import "gomp" + +func main() { + engine := gomp.NewDesktopEngine([]gomp.Scene{}) + engine.Run(60) +} diff --git a/examples/new-api/scenes/main.go b/examples/new-api/scenes/main.go new file mode 100644 index 00000000..15c2b6ac --- /dev/null +++ b/examples/new-api/scenes/main.go @@ -0,0 +1,47 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package scenes + +import ( + "gomp" + "gomp/examples/new-api/systems" + "gomp/pkg/ecs" + "time" +) + +func NewMainScene(world *ecs.World, components *gomp.DesktopComponents) *MainScene { + return &MainScene{ + playerSystem: systems.NewPlayerSystem(world, components.SpriteMatrix, components.Position, components.Rotation, components.Scale, components.AnimationPlayer, components.AnimationState, components.Tint, components.Flip), + } +} + +type MainScene struct { + playerSystem *systems.PlayerSystem +} + +func (s *MainScene) Init() { + s.playerSystem.Init() +} + +func (s *MainScene) Update(dt time.Duration) { + s.playerSystem.Run(dt) +} + +func (s *MainScene) FixedUpdate() { +} + +func (s *MainScene) Destroy() { + s.playerSystem.Destroy() +} diff --git a/scene.go b/scene.go new file mode 100644 index 00000000..6f5dd57b --- /dev/null +++ b/scene.go @@ -0,0 +1,38 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import "time" + +type Scene struct { + IsInitialized bool + Enabled bool + Systems AnyEngineSystemSet +} + +func (s *Scene) Init() { + s.Systems.Init() + s.IsInitialized = true +} +func (s *Scene) Update(dt time.Duration) { + s.Systems.Update(dt) +} +func (s *Scene) FixedUpdate(dt time.Duration) { + s.Systems.FixedUpdate(dt) +} +func (s *Scene) Destroy() { + s.Systems.Destroy() + s.IsInitialized = false +} From 90aaba4dc37b0ccb5d0809c876a178a214d72abf Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 17 Feb 2025 13:21:28 +0300 Subject: [PATCH 005/196] feat scenes --- desktop-components.go | 4 +- desktop-game.go | 161 ++++++++++++++++++ desktop-systems.go | 134 +-------------- engine.go | 30 ++-- examples/new-api/components/components.go | 20 ++- examples/new-api/components/ids.go | 22 +++ examples/new-api/game.go | 26 ++- examples/new-api/scenes/ids.go | 22 +++ .../new-api/scenes/{main.go => main-scene.go} | 23 ++- .../new-api/scenes/scenes.go | 16 +- scene.go | 26 +-- stdsystems/render.go | 8 +- 12 files changed, 300 insertions(+), 192 deletions(-) create mode 100644 desktop-game.go create mode 100644 examples/new-api/components/ids.go create mode 100644 examples/new-api/scenes/ids.go rename examples/new-api/scenes/{main.go => main-scene.go} (71%) rename desktop.go => examples/new-api/scenes/scenes.go (53%) diff --git a/desktop-components.go b/desktop-components.go index c6b93711..93147ce0 100644 --- a/desktop-components.go +++ b/desktop-components.go @@ -19,8 +19,8 @@ import ( "gomp/stdcomponents" ) -func NewDesktopComponents(world *ecs.World) *DesktopComponents { - return &DesktopComponents{ +func NewDesktopComponents(world *ecs.World) DesktopComponents { + return DesktopComponents{ Position: stdcomponents.NewPositionComponentManager(world), Rotation: stdcomponents.NewRotationComponentManager(world), Scale: stdcomponents.NewScaleComponentManager(world), diff --git a/desktop-game.go b/desktop-game.go new file mode 100644 index 00000000..cd255722 --- /dev/null +++ b/desktop-game.go @@ -0,0 +1,161 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import ( + "github.com/negrel/assert" + "time" +) + +func NewDesktopGame(systems *DesktopSystems, scenes map[SceneId]AnyScene) DesktopGame { + game := DesktopGame{ + Systems: systems, + Scenes: scenes, + } + + return game +} + +type DesktopGame struct { + Scenes map[SceneId]AnyScene + CurrentSceneId SceneId + + Systems *DesktopSystems + shouldDestroy bool +} + +func (g *DesktopGame) Init() { + // Network receive + g.Systems.Network.Init() + g.Systems.NetworkReceive.Init() + + // Scenes + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + scene.Init() + + // Network patches + g.Systems.NetworkSend.Init() + + // Animation + g.Systems.AnimationSpriteMatrix.Init() + g.Systems.AnimationPlayer.Init() + + // Prerender init + g.Systems.TextureRenderSprite.Init() + g.Systems.TextureRenderSpriteSheet.Init() + g.Systems.TextureRenderMatrix.Init() + + // Prerender fill + g.Systems.TextureRenderAnimation.Init() + g.Systems.TextureRenderFlip.Init() + g.Systems.TextureRenderPosition.Init() + g.Systems.TextureRenderRotation.Init() + g.Systems.TextureRenderScale.Init() + g.Systems.TextureRenderTint.Init() + + // Render + g.Systems.Render.Init() + g.Systems.Debug.Init() + g.Systems.AssetLib.Init() +} + +func (g *DesktopGame) Update(dt time.Duration) { + // Network receive + g.Systems.Network.Run(dt) + g.Systems.NetworkReceive.Run(dt) + + // Scenes + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + g.CurrentSceneId = scene.Update(dt) + + // Network patches + g.Systems.NetworkSend.Run(dt) + + // Animation + g.Systems.AnimationSpriteMatrix.Run(dt) + g.Systems.AnimationPlayer.Run(dt) + + // Prerender init + g.Systems.TextureRenderSprite.Run(dt) + g.Systems.TextureRenderSpriteSheet.Run(dt) + g.Systems.TextureRenderMatrix.Run(dt) + + // Prerender fill + g.Systems.TextureRenderAnimation.Run(dt) + g.Systems.TextureRenderFlip.Run(dt) + g.Systems.TextureRenderPosition.Run(dt) + g.Systems.TextureRenderRotation.Run(dt) + g.Systems.TextureRenderScale.Run(dt) + g.Systems.TextureRenderTint.Run(dt) + + // Render + g.Systems.AssetLib.Run(dt) + g.Systems.Debug.Run(dt) + if err := g.Systems.Render.Run(dt); err != nil { + g.shouldDestroy = true + } +} + +func (g *DesktopGame) FixedUpdate(dt time.Duration) { + // Scenes + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + scene.FixedUpdate(dt) + + // Network send + g.Systems.NetworkSend.Run(dt) +} + +func (g *DesktopGame) Destroy() { + // Network intents + g.Systems.Network.Destroy() + g.Systems.NetworkReceive.Destroy() + + // Scenes + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + scene.Destroy() + + // Network patches + g.Systems.NetworkSend.Destroy() + + // Animation + g.Systems.AnimationSpriteMatrix.Destroy() + g.Systems.AnimationPlayer.Destroy() + + // Prerender init + g.Systems.TextureRenderSprite.Destroy() + g.Systems.TextureRenderSpriteSheet.Destroy() + g.Systems.TextureRenderMatrix.Destroy() + + // Prerender fill + g.Systems.TextureRenderAnimation.Destroy() + g.Systems.TextureRenderFlip.Destroy() + g.Systems.TextureRenderPosition.Destroy() + g.Systems.TextureRenderRotation.Destroy() + g.Systems.TextureRenderScale.Destroy() + g.Systems.TextureRenderTint.Destroy() + + // Render + g.Systems.Debug.Destroy() + g.Systems.AssetLib.Destroy() + g.Systems.Render.Destroy() +} + +func (g *DesktopGame) ShouldDestroy() bool { + return g.shouldDestroy +} diff --git a/desktop-systems.go b/desktop-systems.go index 4abd59e8..444fa450 100644 --- a/desktop-systems.go +++ b/desktop-systems.go @@ -18,13 +18,11 @@ import ( "gomp/examples/new-api/assets" "gomp/pkg/ecs" "gomp/stdsystems" - "time" ) -func NewDesktopSystems(world *ecs.World, components *DesktopComponents, scenes []Scene) *DesktopSystems { - return &DesktopSystems{ - Scenes: scenes, - Debug: stdsystems.NewDebugSystem(), +func NewDesktopSystems(world *ecs.World, components *DesktopComponents) DesktopSystems { + return DesktopSystems{ + Debug: stdsystems.NewDebugSystem(), Network: stdsystems.NewNetworkSystem(), NetworkReceive: stdsystems.NewNetworkReceiveSystem(), @@ -50,8 +48,7 @@ func NewDesktopSystems(world *ecs.World, components *DesktopComponents, scenes [ } type DesktopSystems struct { - Scenes []Scene - Debug *stdsystems.DebugSystem + Debug *stdsystems.DebugSystem // Network Network *stdsystems.NetworkSystem NetworkReceive *stdsystems.NetworkReceiveSystem @@ -74,126 +71,3 @@ type DesktopSystems struct { AssetLib *stdsystems.AssetLibSystem Render *stdsystems.RenderSystem } - -func (s *DesktopSystems) Init() { - // Network receive - s.Network.Init() - s.NetworkReceive.Init() - - // Network patches - s.NetworkSend.Init() - - // Scenes - for i := range s.Scenes { - if s.Scenes[i].Enabled { - s.Scenes[i].Init() - } - } - - // Animation - s.AnimationSpriteMatrix.Init() - s.AnimationPlayer.Init() - - // Prerender init - s.TextureRenderSprite.Init() - s.TextureRenderSpriteSheet.Init() - s.TextureRenderMatrix.Init() - - // Prerender fill - s.TextureRenderAnimation.Init() - s.TextureRenderFlip.Init() - s.TextureRenderPosition.Init() - s.TextureRenderRotation.Init() - s.TextureRenderScale.Init() - s.TextureRenderTint.Init() - - // Render - s.Render.Init() - s.Debug.Init() - s.AssetLib.Init() -} - -func (s *DesktopSystems) Update(dt time.Duration) { - // Network receive - s.Network.Run(dt) - s.NetworkReceive.Run(dt) - - // Scenes - for i := range s.Scenes { - if s.Scenes[i].Enabled { - s.Scenes[i].Update(dt) - } - } - - // Animation - s.AnimationSpriteMatrix.Run(dt) - s.AnimationPlayer.Run(dt) - - // Prerender init - s.TextureRenderSprite.Run(dt) - s.TextureRenderSpriteSheet.Run(dt) - s.TextureRenderMatrix.Run(dt) - - // Prerender fill - s.TextureRenderAnimation.Run(dt) - s.TextureRenderFlip.Run(dt) - s.TextureRenderPosition.Run(dt) - s.TextureRenderRotation.Run(dt) - s.TextureRenderScale.Run(dt) - s.TextureRenderTint.Run(dt) - - // Render - s.AssetLib.Run(dt) - s.Debug.Run(dt) - s.Render.Run(dt) -} - -func (s *DesktopSystems) FixedUpdate(dt time.Duration) { - // Scenes - for i := range s.Scenes { - if s.Scenes[i].Enabled { - s.Scenes[i].FixedUpdate(dt) - } - } - - // Network send - s.NetworkSend.Run(dt) -} - -func (s *DesktopSystems) Destroy() { - // Network intents - s.Network.Destroy() - s.NetworkReceive.Destroy() - - // Scenes - for i := range s.Scenes { - if s.Scenes[i].IsInitialized { - s.Scenes[i].Destroy() - } - } - - // Network patches - s.NetworkSend.Destroy() - - // Animation - s.AnimationSpriteMatrix.Destroy() - s.AnimationPlayer.Destroy() - - // Prerender init - s.TextureRenderSprite.Destroy() - s.TextureRenderSpriteSheet.Destroy() - s.TextureRenderMatrix.Destroy() - - // Prerender fill - s.TextureRenderAnimation.Destroy() - s.TextureRenderFlip.Destroy() - s.TextureRenderPosition.Destroy() - s.TextureRenderRotation.Destroy() - s.TextureRenderScale.Destroy() - s.TextureRenderTint.Destroy() - - // Render - s.Debug.Destroy() - s.AssetLib.Destroy() - s.Render.Destroy() -} diff --git a/engine.go b/engine.go index 6584a393..e24321cf 100644 --- a/engine.go +++ b/engine.go @@ -15,34 +15,30 @@ Thank you for your support! package gomp import ( - "gomp/pkg/ecs" "time" ) -type AnyEngineSystemSet interface { +type AnyGame interface { Init() Update(dt time.Duration) FixedUpdate(dt time.Duration) Destroy() + ShouldDestroy() bool } -func NewEngine[C any, S AnyEngineSystemSet](world *ecs.World, components C, systems S) *Engine[C, S] { - newGame := Engine[C, S]{ - World: world, - Components: components, - Systems: systems, +func NewEngine(game AnyGame) *Engine { + newGame := Engine{ + Game: game, } return &newGame } -type Engine[C any, S AnyEngineSystemSet] struct { - World *ecs.World - Components C - Systems S +type Engine struct { + Game AnyGame } -func (g *Engine[C, S]) Run(tickrate uint) { +func (e *Engine) Run(tickrate uint) { duration := time.Second / time.Duration(tickrate) ticker := time.NewTicker(duration) @@ -54,10 +50,10 @@ func (g *Engine[C, S]) Run(tickrate uint) { fixedDt time.Duration ) - g.Systems.Init() - defer g.Systems.Destroy() + e.Game.Init() + defer e.Game.Destroy() - for !g.World.ShouldDestroy() { + for !e.Game.ShouldDestroy() { needFixedUpdate := true for needFixedUpdate { select { @@ -65,12 +61,12 @@ func (g *Engine[C, S]) Run(tickrate uint) { needFixedUpdate = false case <-ticker.C: t = time.Now() - g.Systems.FixedUpdate(fixedDt) + e.Game.FixedUpdate(fixedDt) fixedDt = time.Since(t) } } t = time.Now() - g.Systems.Update(dt) + e.Game.Update(dt) dt = time.Since(t) } } diff --git a/examples/new-api/components/components.go b/examples/new-api/components/components.go index 50dc67db..8a6bb626 100644 --- a/examples/new-api/components/components.go +++ b/examples/new-api/components/components.go @@ -14,9 +14,19 @@ Thank you for your support! package components -import "gomp/pkg/ecs" - -const ( - INVALID_ID ecs.ComponentID = iota - HEALTH_ID +import ( + "gomp" + "gomp/pkg/ecs" ) + +type GameComponents struct { + *gomp.DesktopComponents + Health *HealthComponentManager +} + +func NewGameComponents(world *ecs.World, desktopComponents *gomp.DesktopComponents) GameComponents { + return GameComponents{ + DesktopComponents: desktopComponents, + Health: NewHealthComponentManager(world), + } +} diff --git a/examples/new-api/components/ids.go b/examples/new-api/components/ids.go new file mode 100644 index 00000000..50dc67db --- /dev/null +++ b/examples/new-api/components/ids.go @@ -0,0 +1,22 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package components + +import "gomp/pkg/ecs" + +const ( + INVALID_ID ecs.ComponentID = iota + HEALTH_ID +) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 5343e407..325106b4 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -14,9 +14,31 @@ Thank you for your support! package main -import "gomp" +import ( + "gomp" + "gomp/examples/new-api/components" + "gomp/examples/new-api/scenes" + "gomp/pkg/ecs" +) func main() { - engine := gomp.NewDesktopEngine([]gomp.Scene{}) + // Initializing ECS world + world := ecs.CreateWorld("main") + defer world.Destroy() + + // Initializing components + desktopComponents := gomp.NewDesktopComponents(world) + gameComponents := components.NewGameComponents(world, &desktopComponents) + + // Initializing scenes + sceneSet := scenes.NewSceneSet(world, &gameComponents) + + // Initializing systems + systems := gomp.NewDesktopSystems(world, &desktopComponents) + + game := gomp.NewDesktopGame(&systems, sceneSet) + game.CurrentSceneId = scenes.MainSceneId + + engine := gomp.NewEngine(&game) engine.Run(60) } diff --git a/examples/new-api/scenes/ids.go b/examples/new-api/scenes/ids.go new file mode 100644 index 00000000..6ef30032 --- /dev/null +++ b/examples/new-api/scenes/ids.go @@ -0,0 +1,22 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package scenes + +import "gomp" + +const ( + MenuSceneId gomp.SceneId = iota + MainSceneId +) diff --git a/examples/new-api/scenes/main.go b/examples/new-api/scenes/main-scene.go similarity index 71% rename from examples/new-api/scenes/main.go rename to examples/new-api/scenes/main-scene.go index 15c2b6ac..b986acd1 100644 --- a/examples/new-api/scenes/main.go +++ b/examples/new-api/scenes/main-scene.go @@ -16,12 +16,13 @@ package scenes import ( "gomp" + "gomp/examples/new-api/components" "gomp/examples/new-api/systems" "gomp/pkg/ecs" "time" ) -func NewMainScene(world *ecs.World, components *gomp.DesktopComponents) *MainScene { +func NewMainScene(world *ecs.World, components *components.GameComponents) *MainScene { return &MainScene{ playerSystem: systems.NewPlayerSystem(world, components.SpriteMatrix, components.Position, components.Rotation, components.Scale, components.AnimationPlayer, components.AnimationState, components.Tint, components.Flip), } @@ -35,13 +36,25 @@ func (s *MainScene) Init() { s.playerSystem.Init() } -func (s *MainScene) Update(dt time.Duration) { +func (s *MainScene) Destroy() { + s.playerSystem.Destroy() +} + +func (s *MainScene) Update(dt time.Duration) gomp.SceneId { s.playerSystem.Run(dt) + return MainSceneId } -func (s *MainScene) FixedUpdate() { +func (s *MainScene) FixedUpdate(dt time.Duration) { + } -func (s *MainScene) Destroy() { - s.playerSystem.Destroy() +func (s *MainScene) OnEnter() { + +} + +func (s *MainScene) OnExit() { + } + +var _ gomp.AnyScene = (*MainScene)(nil) diff --git a/desktop.go b/examples/new-api/scenes/scenes.go similarity index 53% rename from desktop.go rename to examples/new-api/scenes/scenes.go index c3c6ef3d..8d358e7e 100644 --- a/desktop.go +++ b/examples/new-api/scenes/scenes.go @@ -12,20 +12,18 @@ none :) Thank you for your support! */ -package gomp +package scenes import ( + "gomp" + "gomp/examples/new-api/components" "gomp/pkg/ecs" ) -type DesktopEngine = Engine[*DesktopComponents, *DesktopSystems] +func NewSceneSet(world *ecs.World, components *components.GameComponents) map[gomp.SceneId]gomp.AnyScene { + sceneSet := make(map[gomp.SceneId]gomp.AnyScene) -func NewDesktopEngine(scenes []Scene) *DesktopEngine { - world := ecs.CreateWorld("main") - defer world.Destroy() + sceneSet[MainSceneId] = NewMainScene(world, components) - components := NewDesktopComponents(world) - systems := NewDesktopSystems(world, components, scenes) - - return NewEngine(world, components, systems) + return sceneSet } diff --git a/scene.go b/scene.go index 6f5dd57b..566d76cf 100644 --- a/scene.go +++ b/scene.go @@ -16,23 +16,13 @@ package gomp import "time" -type Scene struct { - IsInitialized bool - Enabled bool - Systems AnyEngineSystemSet -} +type SceneId uint16 -func (s *Scene) Init() { - s.Systems.Init() - s.IsInitialized = true -} -func (s *Scene) Update(dt time.Duration) { - s.Systems.Update(dt) -} -func (s *Scene) FixedUpdate(dt time.Duration) { - s.Systems.FixedUpdate(dt) -} -func (s *Scene) Destroy() { - s.Systems.Destroy() - s.IsInitialized = false +type AnyScene interface { + Init() + Destroy() + Update(dt time.Duration) SceneId + FixedUpdate(dt time.Duration) + OnEnter() + OnExit() } diff --git a/stdsystems/render.go b/stdsystems/render.go index 6e919136..bd4b9725 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -27,14 +27,13 @@ func NewRenderSystem(world *ecs.World, textureRenders *ecs.ComponentManager[stdc } func (s *RenderSystem) Init() { - rl.InitWindow(800, 600, "raylib [core] ebiten-ecs - basic window") + rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) rl.SetTargetFPS(currentMonitorRefreshRate) } -func (s *RenderSystem) Run(dt time.Duration) { +func (s *RenderSystem) Run(dt time.Duration) error { if rl.WindowShouldClose() { - s.world.SetShouldDestroy(true) - return + return fmt.Errorf("window closed") } rl.BeginDrawing() @@ -54,6 +53,7 @@ func (s *RenderSystem) Run(dt time.Duration) { rl.DrawText(fmt.Sprintf("%s", s.world.DtFixedUpdate()), 10, 70, 20, rl.Red) rl.EndDrawing() + return nil } func (s *RenderSystem) Destroy() { From 97fed6d765ee4179542751ac0abeb8f6fc7f408e Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 17 Feb 2025 14:57:43 +0300 Subject: [PATCH 006/196] refactor playerSystem --- desktop-components.go | 2 + desktop-game.go | 27 ++++++---- desktop-systems.go | 5 ++ engine.go | 67 ++++++++++++++++--------- examples/new-api/entities/player.go | 6 +++ examples/new-api/game.go | 2 +- examples/new-api/scenes/main-scene.go | 2 +- examples/new-api/systems/player.go | 18 +++++-- stdcomponents/{components.go => ids.go} | 1 + stdcomponents/velocity.go | 27 ++++++++++ stdsystems/render.go | 4 +- stdsystems/velocity.go | 39 ++++++++++++++ 12 files changed, 155 insertions(+), 45 deletions(-) rename stdcomponents/{components.go => ids.go} (97%) create mode 100644 stdcomponents/velocity.go create mode 100644 stdsystems/velocity.go diff --git a/desktop-components.go b/desktop-components.go index 93147ce0..5db49a89 100644 --- a/desktop-components.go +++ b/desktop-components.go @@ -24,6 +24,7 @@ func NewDesktopComponents(world *ecs.World) DesktopComponents { Position: stdcomponents.NewPositionComponentManager(world), Rotation: stdcomponents.NewRotationComponentManager(world), Scale: stdcomponents.NewScaleComponentManager(world), + Velocity: stdcomponents.NewVelocityComponentManager(world), Flip: stdcomponents.NewFlipComponentManager(world), Sprite: stdcomponents.NewSpriteComponentManager(world), SpriteSheet: stdcomponents.NewSpriteSheetComponentManager(world), @@ -40,6 +41,7 @@ type DesktopComponents struct { Position *stdcomponents.PositionComponentManager Rotation *stdcomponents.RotationComponentManager Scale *stdcomponents.ScaleComponentManager + Velocity *stdcomponents.VelocityComponentManager Flip *stdcomponents.FlipComponentManager Sprite *stdcomponents.SpriteComponentManager SpriteSheet *stdcomponents.SpriteSheetComponentManager diff --git a/desktop-game.go b/desktop-game.go index cd255722..1f12f4d5 100644 --- a/desktop-game.go +++ b/desktop-game.go @@ -85,6 +85,22 @@ func (g *DesktopGame) Update(dt time.Duration) { // Network patches g.Systems.NetworkSend.Run(dt) + g.Systems.Debug.Run(dt) +} + +func (g *DesktopGame) FixedUpdate(dt time.Duration) { + // Scenes + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + scene.FixedUpdate(dt) + + // Network send + g.Systems.NetworkSend.Run(dt) +} + +func (g *DesktopGame) Render(dt time.Duration) { + g.Systems.Velocity.Run(dt) + // Animation g.Systems.AnimationSpriteMatrix.Run(dt) g.Systems.AnimationPlayer.Run(dt) @@ -104,22 +120,11 @@ func (g *DesktopGame) Update(dt time.Duration) { // Render g.Systems.AssetLib.Run(dt) - g.Systems.Debug.Run(dt) if err := g.Systems.Render.Run(dt); err != nil { g.shouldDestroy = true } } -func (g *DesktopGame) FixedUpdate(dt time.Duration) { - // Scenes - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - scene.FixedUpdate(dt) - - // Network send - g.Systems.NetworkSend.Run(dt) -} - func (g *DesktopGame) Destroy() { // Network intents g.Systems.Network.Destroy() diff --git a/desktop-systems.go b/desktop-systems.go index 444fa450..562bdee1 100644 --- a/desktop-systems.go +++ b/desktop-systems.go @@ -24,6 +24,8 @@ func NewDesktopSystems(world *ecs.World, components *DesktopComponents) DesktopS return DesktopSystems{ Debug: stdsystems.NewDebugSystem(), + Velocity: stdsystems.NewVelocitySystem(components.Velocity, components.Position), + Network: stdsystems.NewNetworkSystem(), NetworkReceive: stdsystems.NewNetworkReceiveSystem(), NetworkSend: stdsystems.NewNetworkSendSystem(world, components.Position, components.Rotation, components.Flip), @@ -49,6 +51,9 @@ func NewDesktopSystems(world *ecs.World, components *DesktopComponents) DesktopS type DesktopSystems struct { Debug *stdsystems.DebugSystem + + Velocity *stdsystems.VelocitySystem + // Network Network *stdsystems.NetworkSystem NetworkReceive *stdsystems.NetworkReceiveSystem diff --git a/engine.go b/engine.go index e24321cf..51e60127 100644 --- a/engine.go +++ b/engine.go @@ -22,51 +22,68 @@ type AnyGame interface { Init() Update(dt time.Duration) FixedUpdate(dt time.Duration) + Render(dt time.Duration) Destroy() ShouldDestroy() bool } -func NewEngine(game AnyGame) *Engine { - newGame := Engine{ +func NewEngine(game AnyGame) Engine { + engine := Engine{ Game: game, } - return &newGame + return engine } type Engine struct { Game AnyGame + + lastUpdateAt time.Time + lastFixedUpdateAt time.Time + lastRenderAt time.Time } -func (e *Engine) Run(tickrate uint) { - duration := time.Second / time.Duration(tickrate) +func (e *Engine) Run(tickrate uint, framerate uint) { + fixedUpdDuration := time.Second / time.Duration(tickrate) + framerateDuration := time.Second / time.Duration(framerate) - ticker := time.NewTicker(duration) - defer ticker.Stop() + fixedUpdTicker := time.NewTicker(fixedUpdDuration) + defer fixedUpdTicker.Stop() - var ( - t time.Time - dt time.Duration - fixedDt time.Duration - ) + renderTicker := time.NewTicker(framerateDuration) + defer renderTicker.Stop() e.Game.Init() defer e.Game.Destroy() + e.lastUpdateAt = time.Now() + e.lastFixedUpdateAt = time.Now() + e.lastRenderAt = time.Now() + for !e.Game.ShouldDestroy() { - needFixedUpdate := true - for needFixedUpdate { - select { - default: - needFixedUpdate = false - case <-ticker.C: - t = time.Now() - e.Game.FixedUpdate(fixedDt) - fixedDt = time.Since(t) - } - } - t = time.Now() + // Update + dt := time.Since(e.lastUpdateAt) e.Game.Update(dt) - dt = time.Since(t) + e.lastUpdateAt = time.Now() + + // Fixed Update + select { + case <-fixedUpdTicker.C: + dt := time.Since(e.lastFixedUpdateAt) + e.Game.FixedUpdate(dt) + e.lastFixedUpdateAt = time.Now() + default: + break + } + + // Render + select { + case <-renderTicker.C: + dt := time.Since(e.lastRenderAt) + e.Game.Render(dt) + e.lastRenderAt = time.Now() + default: + break + } } } diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index 14b29141..36e0eb1b 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -28,6 +28,7 @@ type Player struct { Position *stdcomponents.Position Rotation *stdcomponents.Rotation Scale *stdcomponents.Scale + Velocity *stdcomponents.Velocity SpriteMatrix *stdcomponents.SpriteMatrix Tint *stdcomponents.Tint AnimationPlayer *stdcomponents.AnimationPlayer @@ -70,6 +71,7 @@ func CreatePlayer( positions *stdcomponents.PositionComponentManager, rotations *stdcomponents.RotationComponentManager, scales *stdcomponents.ScaleComponentManager, + velocities *stdcomponents.VelocityComponentManager, animationPlayers *stdcomponents.AnimationPlayerComponentManager, animationStates *stdcomponents.AnimationStateComponentManager, tints *stdcomponents.TintComponentManager, @@ -95,6 +97,10 @@ func CreatePlayer( } player.Scale = scales.Create(entity, scale) + // Adding velocity component + velocity := stdcomponents.Velocity{} + player.Velocity = velocities.Create(entity, velocity) + // Adding Tint component tint := stdcomponents.Tint{R: 255, G: 255, B: 255, A: 255} player.Tint = tints.Create(entity, tint) diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 325106b4..2694e154 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -40,5 +40,5 @@ func main() { game.CurrentSceneId = scenes.MainSceneId engine := gomp.NewEngine(&game) - engine.Run(60) + engine.Run(30, 165) } diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index b986acd1..9a0bb122 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -24,7 +24,7 @@ import ( func NewMainScene(world *ecs.World, components *components.GameComponents) *MainScene { return &MainScene{ - playerSystem: systems.NewPlayerSystem(world, components.SpriteMatrix, components.Position, components.Rotation, components.Scale, components.AnimationPlayer, components.AnimationState, components.Tint, components.Flip), + playerSystem: systems.NewPlayerSystem(world, components.SpriteMatrix, components.Position, components.Rotation, components.Scale, components.Velocity, components.AnimationPlayer, components.AnimationState, components.Tint, components.Flip), } } diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index c315c075..b0999daf 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -20,6 +20,7 @@ func NewPlayerSystem( positions *stdcomponents.PositionComponentManager, rotations *stdcomponents.RotationComponentManager, scales *stdcomponents.ScaleComponentManager, + velocities *stdcomponents.VelocityComponentManager, animationPlayers *stdcomponents.AnimationPlayerComponentManager, animationStates *stdcomponents.AnimationStateComponentManager, tints *stdcomponents.TintComponentManager, @@ -31,6 +32,7 @@ func NewPlayerSystem( positions: positions, rotations: rotations, scales: scales, + velocities: velocities, animationPlayers: animationPlayers, animationStates: animationStates, tints: tints, @@ -45,6 +47,7 @@ type PlayerSystem struct { positions *stdcomponents.PositionComponentManager rotations *stdcomponents.RotationComponentManager scales *stdcomponents.ScaleComponentManager + velocities *stdcomponents.VelocityComponentManager animationPlayers *stdcomponents.AnimationPlayerComponentManager animationStates *stdcomponents.AnimationStateComponentManager tints *stdcomponents.TintComponentManager @@ -52,34 +55,39 @@ type PlayerSystem struct { } func (s *PlayerSystem) Init() { - s.player = entities.CreatePlayer(s.world, s.spriteMatrixes, s.positions, s.rotations, s.scales, s.animationPlayers, s.animationStates, s.tints, s.flips) + s.player = entities.CreatePlayer(s.world, s.spriteMatrixes, s.positions, s.rotations, s.scales, s.velocities, s.animationPlayers, s.animationStates, s.tints, s.flips) s.player.Position.X = 100 s.player.Position.Y = 100 } func (s *PlayerSystem) Run(dt time.Duration) { animationState := s.animationStates.Get(s.player.Entity) + var speed float32 = 300 + + s.player.Velocity.X = 0 + s.player.Velocity.Y = 0 + if rl.IsKeyDown(rl.KeySpace) { *animationState = entities.PlayerStateJump } else { *animationState = entities.PlayerStateIdle if rl.IsKeyDown(rl.KeyD) { *animationState = entities.PlayerStateWalk - s.player.Position.X++ + s.player.Velocity.X = speed s.player.Flip.X = false } if rl.IsKeyDown(rl.KeyA) { *animationState = entities.PlayerStateWalk - s.player.Position.X-- + s.player.Velocity.X = -speed s.player.Flip.X = true } if rl.IsKeyDown(rl.KeyW) { *animationState = entities.PlayerStateWalk - s.player.Position.Y-- + s.player.Velocity.Y = -speed } if rl.IsKeyDown(rl.KeyS) { *animationState = entities.PlayerStateWalk - s.player.Position.Y++ + s.player.Velocity.Y = speed } } } diff --git a/stdcomponents/components.go b/stdcomponents/ids.go similarity index 97% rename from stdcomponents/components.go rename to stdcomponents/ids.go index 252fd87c..c4c4595f 100644 --- a/stdcomponents/components.go +++ b/stdcomponents/ids.go @@ -16,6 +16,7 @@ const ( ROTATION_ID SCALE_ID FLIP_ID + VELOCITY_ID SPRITE_ID SPRITE_SHEET_ID SPRITE_MATRIX_ID diff --git a/stdcomponents/velocity.go b/stdcomponents/velocity.go new file mode 100644 index 00000000..fd8843ce --- /dev/null +++ b/stdcomponents/velocity.go @@ -0,0 +1,27 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package stdcomponents + +import "gomp/pkg/ecs" + +type Velocity struct { + X, Y float32 +} + +type VelocityComponentManager = ecs.ComponentManager[Velocity] + +func NewVelocityComponentManager(world *ecs.World) *VelocityComponentManager { + return ecs.NewComponentManager[Velocity](world, VELOCITY_ID) +} diff --git a/stdsystems/render.go b/stdsystems/render.go index bd4b9725..61fdf48d 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -28,8 +28,8 @@ func NewRenderSystem(world *ecs.World, textureRenders *ecs.ComponentManager[stdc func (s *RenderSystem) Init() { rl.InitWindow(1024, 768, "raylib [core] ebiten-ecs - basic window") - currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) - rl.SetTargetFPS(currentMonitorRefreshRate) + //currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) + //rl.SetTargetFPS(currentMonitorRefreshRate) } func (s *RenderSystem) Run(dt time.Duration) error { if rl.WindowShouldClose() { diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go new file mode 100644 index 00000000..f4d0cde2 --- /dev/null +++ b/stdsystems/velocity.go @@ -0,0 +1,39 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +package stdsystems + +import ( + "gomp/pkg/ecs" + "gomp/stdcomponents" + "time" +) + +func NewVelocitySystem( + velocities *stdcomponents.VelocityComponentManager, + positions *stdcomponents.PositionComponentManager, +) *VelocitySystem { + return &VelocitySystem{ + velocities: velocities, + positions: positions, + } +} + +type VelocitySystem struct { + velocities *stdcomponents.VelocityComponentManager + positions *stdcomponents.PositionComponentManager +} + +func (s *VelocitySystem) Init() {} +func (s *VelocitySystem) Run(dt time.Duration) { + s.velocities.AllParallel(func(entity ecs.Entity, v *stdcomponents.Velocity) bool { + position := s.positions.Get(entity) + position.X += v.X * float32(dt.Seconds()) + position.Y += v.Y * float32(dt.Seconds()) + return true + }) +} +func (s *VelocitySystem) Destroy() {} From abd6532c8b7e529b2684fb7e2ae0b17062cfb4cd Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Mon, 17 Feb 2025 15:00:59 +0300 Subject: [PATCH 007/196] init velocity system in desktop-game.go --- desktop-game.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/desktop-game.go b/desktop-game.go index 1f12f4d5..fd718906 100644 --- a/desktop-game.go +++ b/desktop-game.go @@ -46,6 +46,8 @@ func (g *DesktopGame) Init() { assert.True(ok, "Scene not found") scene.Init() + g.Systems.Velocity.Init() + // Network patches g.Systems.NetworkSend.Init() From b4ed6e1753c01e237d1e4ca996d461f42ad52eeb Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 18 Feb 2025 18:39:29 +0300 Subject: [PATCH 008/196] refactor scenes and implement dynamic auto imports for components in systems --- desktop-game.go | 168 ------------------- desktop-systems.go | 78 --------- examples/new-api/components/components.go | 32 ---- examples/new-api/game.go | 20 +-- examples/new-api/instances/component-list.go | 57 +++++++ examples/new-api/instances/system-list.go | 136 +++++++++++++++ examples/new-api/scenes/main-scene.go | 138 +++++++++++++-- examples/new-api/scenes/scenes.go | 29 ---- examples/new-api/systems/player.go | 72 +++----- game.go | 109 ++++++++++++ pkg/ecs/ecs.go | 4 +- scene.go | 8 +- stdsystems/animation-player.go | 12 +- stdsystems/animation-spritematrix.go | 26 +-- stdsystems/asset-library.go | 4 +- stdsystems/debug.go | 4 +- stdsystems/network-receive.go | 4 +- stdsystems/network-send.go | 33 ++-- stdsystems/network.go | 4 +- stdsystems/render.go | 25 ++- stdsystems/texture-render-animation.go | 22 +-- stdsystems/texture-render-flip.go | 18 +- stdsystems/texture-render-position.go | 15 +- stdsystems/texture-render-rotation.go | 15 +- stdsystems/texture-render-scale.go | 15 +- stdsystems/texture-render-sprite.go | 20 +-- stdsystems/texture-render-spritematrix.go | 26 +-- stdsystems/texture-render-spritesheet.go | 20 +-- stdsystems/texture-render-tint.go | 15 +- stdsystems/velocity.go | 18 +- 30 files changed, 577 insertions(+), 570 deletions(-) delete mode 100644 desktop-game.go delete mode 100644 desktop-systems.go delete mode 100644 examples/new-api/components/components.go create mode 100644 examples/new-api/instances/component-list.go create mode 100644 examples/new-api/instances/system-list.go delete mode 100644 examples/new-api/scenes/scenes.go create mode 100644 game.go diff --git a/desktop-game.go b/desktop-game.go deleted file mode 100644 index fd718906..00000000 --- a/desktop-game.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package gomp - -import ( - "github.com/negrel/assert" - "time" -) - -func NewDesktopGame(systems *DesktopSystems, scenes map[SceneId]AnyScene) DesktopGame { - game := DesktopGame{ - Systems: systems, - Scenes: scenes, - } - - return game -} - -type DesktopGame struct { - Scenes map[SceneId]AnyScene - CurrentSceneId SceneId - - Systems *DesktopSystems - shouldDestroy bool -} - -func (g *DesktopGame) Init() { - // Network receive - g.Systems.Network.Init() - g.Systems.NetworkReceive.Init() - - // Scenes - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - scene.Init() - - g.Systems.Velocity.Init() - - // Network patches - g.Systems.NetworkSend.Init() - - // Animation - g.Systems.AnimationSpriteMatrix.Init() - g.Systems.AnimationPlayer.Init() - - // Prerender init - g.Systems.TextureRenderSprite.Init() - g.Systems.TextureRenderSpriteSheet.Init() - g.Systems.TextureRenderMatrix.Init() - - // Prerender fill - g.Systems.TextureRenderAnimation.Init() - g.Systems.TextureRenderFlip.Init() - g.Systems.TextureRenderPosition.Init() - g.Systems.TextureRenderRotation.Init() - g.Systems.TextureRenderScale.Init() - g.Systems.TextureRenderTint.Init() - - // Render - g.Systems.Render.Init() - g.Systems.Debug.Init() - g.Systems.AssetLib.Init() -} - -func (g *DesktopGame) Update(dt time.Duration) { - // Network receive - g.Systems.Network.Run(dt) - g.Systems.NetworkReceive.Run(dt) - - // Scenes - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - g.CurrentSceneId = scene.Update(dt) - - // Network patches - g.Systems.NetworkSend.Run(dt) - - g.Systems.Debug.Run(dt) -} - -func (g *DesktopGame) FixedUpdate(dt time.Duration) { - // Scenes - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - scene.FixedUpdate(dt) - - // Network send - g.Systems.NetworkSend.Run(dt) -} - -func (g *DesktopGame) Render(dt time.Duration) { - g.Systems.Velocity.Run(dt) - - // Animation - g.Systems.AnimationSpriteMatrix.Run(dt) - g.Systems.AnimationPlayer.Run(dt) - - // Prerender init - g.Systems.TextureRenderSprite.Run(dt) - g.Systems.TextureRenderSpriteSheet.Run(dt) - g.Systems.TextureRenderMatrix.Run(dt) - - // Prerender fill - g.Systems.TextureRenderAnimation.Run(dt) - g.Systems.TextureRenderFlip.Run(dt) - g.Systems.TextureRenderPosition.Run(dt) - g.Systems.TextureRenderRotation.Run(dt) - g.Systems.TextureRenderScale.Run(dt) - g.Systems.TextureRenderTint.Run(dt) - - // Render - g.Systems.AssetLib.Run(dt) - if err := g.Systems.Render.Run(dt); err != nil { - g.shouldDestroy = true - } -} - -func (g *DesktopGame) Destroy() { - // Network intents - g.Systems.Network.Destroy() - g.Systems.NetworkReceive.Destroy() - - // Scenes - scene, ok := g.Scenes[g.CurrentSceneId] - assert.True(ok, "Scene not found") - scene.Destroy() - - // Network patches - g.Systems.NetworkSend.Destroy() - - // Animation - g.Systems.AnimationSpriteMatrix.Destroy() - g.Systems.AnimationPlayer.Destroy() - - // Prerender init - g.Systems.TextureRenderSprite.Destroy() - g.Systems.TextureRenderSpriteSheet.Destroy() - g.Systems.TextureRenderMatrix.Destroy() - - // Prerender fill - g.Systems.TextureRenderAnimation.Destroy() - g.Systems.TextureRenderFlip.Destroy() - g.Systems.TextureRenderPosition.Destroy() - g.Systems.TextureRenderRotation.Destroy() - g.Systems.TextureRenderScale.Destroy() - g.Systems.TextureRenderTint.Destroy() - - // Render - g.Systems.Debug.Destroy() - g.Systems.AssetLib.Destroy() - g.Systems.Render.Destroy() -} - -func (g *DesktopGame) ShouldDestroy() bool { - return g.shouldDestroy -} diff --git a/desktop-systems.go b/desktop-systems.go deleted file mode 100644 index 562bdee1..00000000 --- a/desktop-systems.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package gomp - -import ( - "gomp/examples/new-api/assets" - "gomp/pkg/ecs" - "gomp/stdsystems" -) - -func NewDesktopSystems(world *ecs.World, components *DesktopComponents) DesktopSystems { - return DesktopSystems{ - Debug: stdsystems.NewDebugSystem(), - - Velocity: stdsystems.NewVelocitySystem(components.Velocity, components.Position), - - Network: stdsystems.NewNetworkSystem(), - NetworkReceive: stdsystems.NewNetworkReceiveSystem(), - NetworkSend: stdsystems.NewNetworkSendSystem(world, components.Position, components.Rotation, components.Flip), - - AnimationSpriteMatrix: stdsystems.NewAnimationSpriteMatrixSystem(world, components.AnimationPlayer, components.AnimationState, components.SpriteMatrix), - AnimationPlayer: stdsystems.NewAnimationPlayerSystem(components.AnimationPlayer), - - TextureRenderSprite: stdsystems.NewTextureRenderSpriteSystem(components.Sprite, components.TextureRender), - TextureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(components.SpriteSheet, components.TextureRender), - TextureRenderMatrix: stdsystems.NewTextureRenderMatrixSystem(components.SpriteMatrix, components.TextureRender, components.AnimationState), - - TextureRenderAnimation: stdsystems.NewTextureRenderAnimationSystem(components.AnimationPlayer, components.TextureRender), - TextureRenderFlip: stdsystems.NewTextureRenderFlipSystem(components.Flip, components.TextureRender), - TextureRenderPosition: stdsystems.NewTextureRenderPositionSystem(components.Position, components.TextureRender), - TextureRenderRotation: stdsystems.NewTextureRenderRotationSystem(components.Rotation, components.TextureRender), - TextureRenderScale: stdsystems.NewTextureRenderScaleSystem(components.Scale, components.TextureRender), - TextureRenderTint: stdsystems.NewTextureRenderTintSystem(components.Tint, components.TextureRender), - - AssetLib: stdsystems.NewAssetLibSystem([]ecs.AnyAssetLibrary{assets.Textures}), - Render: stdsystems.NewRenderSystem(world, components.TextureRender), - } -} - -type DesktopSystems struct { - Debug *stdsystems.DebugSystem - - Velocity *stdsystems.VelocitySystem - - // Network - Network *stdsystems.NetworkSystem - NetworkReceive *stdsystems.NetworkReceiveSystem - NetworkSend *stdsystems.NetworkSendSystem - // Animation - AnimationSpriteMatrix *stdsystems.AnimationSpriteMatrixSystem - AnimationPlayer *stdsystems.AnimationPlayerSystem - // Prerender init - TextureRenderSprite *stdsystems.TextureRenderSpriteSystem - TextureRenderSpriteSheet *stdsystems.TextureRenderSpriteSheetSystem - TextureRenderMatrix *stdsystems.TextureRenderMatrixSystem - // Prerender fill - TextureRenderAnimation *stdsystems.TextureRenderAnimationSystem - TextureRenderFlip *stdsystems.TextureRenderFlipSystem - TextureRenderPosition *stdsystems.TextureRenderPositionSystem - TextureRenderRotation *stdsystems.TextureRenderRotationSystem - TextureRenderScale *stdsystems.TextureRenderScaleSystem - TextureRenderTint *stdsystems.TextureRenderTintSystem - // Render - AssetLib *stdsystems.AssetLibSystem - Render *stdsystems.RenderSystem -} diff --git a/examples/new-api/components/components.go b/examples/new-api/components/components.go deleted file mode 100644 index 8a6bb626..00000000 --- a/examples/new-api/components/components.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package components - -import ( - "gomp" - "gomp/pkg/ecs" -) - -type GameComponents struct { - *gomp.DesktopComponents - Health *HealthComponentManager -} - -func NewGameComponents(world *ecs.World, desktopComponents *gomp.DesktopComponents) GameComponents { - return GameComponents{ - DesktopComponents: desktopComponents, - Health: NewHealthComponentManager(world), - } -} diff --git a/examples/new-api/game.go b/examples/new-api/game.go index 2694e154..05e035fb 100644 --- a/examples/new-api/game.go +++ b/examples/new-api/game.go @@ -16,27 +16,13 @@ package main import ( "gomp" - "gomp/examples/new-api/components" "gomp/examples/new-api/scenes" - "gomp/pkg/ecs" ) func main() { - // Initializing ECS world - world := ecs.CreateWorld("main") - defer world.Destroy() - - // Initializing components - desktopComponents := gomp.NewDesktopComponents(world) - gameComponents := components.NewGameComponents(world, &desktopComponents) - - // Initializing scenes - sceneSet := scenes.NewSceneSet(world, &gameComponents) - - // Initializing systems - systems := gomp.NewDesktopSystems(world, &desktopComponents) - - game := gomp.NewDesktopGame(&systems, sceneSet) + game := gomp.NewGame( + scenes.NewMainScene(), + ) game.CurrentSceneId = scenes.MainSceneId engine := gomp.NewEngine(&game) diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go new file mode 100644 index 00000000..2a63a6cf --- /dev/null +++ b/examples/new-api/instances/component-list.go @@ -0,0 +1,57 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package instances + +import ( + "gomp/examples/new-api/components" + "gomp/pkg/ecs" + "gomp/stdcomponents" +) + +type ComponentList struct { + Position *stdcomponents.PositionComponentManager + Rotation *stdcomponents.RotationComponentManager + Scale *stdcomponents.ScaleComponentManager + Velocity *stdcomponents.VelocityComponentManager + Flip *stdcomponents.FlipComponentManager + Sprite *stdcomponents.SpriteComponentManager + SpriteSheet *stdcomponents.SpriteSheetComponentManager + SpriteMatrix *stdcomponents.SpriteMatrixComponentManager + Tint *stdcomponents.TintComponentManager + AnimationPlayer *stdcomponents.AnimationPlayerComponentManager + AnimationState *stdcomponents.AnimationStateComponentManager + TextureRender *stdcomponents.TextureRenderComponentManager + Network *stdcomponents.NetworkComponentManager + Health *components.HealthComponentManager +} + +func NewComponentList(world *ecs.World) ComponentList { + return ComponentList{ + Position: stdcomponents.NewPositionComponentManager(world), + Rotation: stdcomponents.NewRotationComponentManager(world), + Scale: stdcomponents.NewScaleComponentManager(world), + Velocity: stdcomponents.NewVelocityComponentManager(world), + Flip: stdcomponents.NewFlipComponentManager(world), + Sprite: stdcomponents.NewSpriteComponentManager(world), + SpriteSheet: stdcomponents.NewSpriteSheetComponentManager(world), + SpriteMatrix: stdcomponents.NewSpriteMatrixComponentManager(world), + Tint: stdcomponents.NewTintComponentManager(world), + AnimationPlayer: stdcomponents.NewAnimationPlayerComponentManager(world), + AnimationState: stdcomponents.NewAnimationStateComponentManager(world), + TextureRender: stdcomponents.NewTextureRenderComponentManager(world), + Network: stdcomponents.NewNetworkComponentManager(world), + Health: components.NewHealthComponentManager(world), + } +} diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go new file mode 100644 index 00000000..c1a7e6cf --- /dev/null +++ b/examples/new-api/instances/system-list.go @@ -0,0 +1,136 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package instances + +import ( + "gomp/examples/new-api/assets" + "gomp/examples/new-api/systems" + "gomp/pkg/ecs" + "gomp/stdsystems" + "reflect" +) + +func NewSystemList(world *ecs.World, components *ComponentList) SystemList { + newSystemList := SystemList{ + Player: systems.NewPlayerSystem(), + Debug: stdsystems.NewDebugSystem(), + + Velocity: stdsystems.NewVelocitySystem(), + + Network: stdsystems.NewNetworkSystem(), + NetworkReceive: stdsystems.NewNetworkReceiveSystem(), + NetworkSend: stdsystems.NewNetworkSendSystem(), + + AnimationSpriteMatrix: stdsystems.NewAnimationSpriteMatrixSystem(), + AnimationPlayer: stdsystems.NewAnimationPlayerSystem(), + + TextureRenderSprite: stdsystems.NewTextureRenderSpriteSystem(), + TextureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(), + TextureRenderMatrix: stdsystems.NewTextureRenderMatrixSystem(), + + TextureRenderAnimation: stdsystems.NewTextureRenderAnimationSystem(), + TextureRenderFlip: stdsystems.NewTextureRenderFlipSystem(), + TextureRenderPosition: stdsystems.NewTextureRenderPositionSystem(), + TextureRenderRotation: stdsystems.NewTextureRenderRotationSystem(), + TextureRenderScale: stdsystems.NewTextureRenderScaleSystem(), + TextureRenderTint: stdsystems.NewTextureRenderTintSystem(), + + AssetLib: stdsystems.NewAssetLibSystem([]ecs.AnyAssetLibrary{assets.Textures}), + Render: stdsystems.NewRenderSystem(), + } + + InjectECSToSystems(&newSystemList, world, components) + + return newSystemList +} + +type SystemList struct { + Player systems.PlayerSystem + Debug stdsystems.DebugSystem + + Velocity stdsystems.VelocitySystem + + // Network + Network stdsystems.NetworkSystem + NetworkReceive stdsystems.NetworkReceiveSystem + NetworkSend stdsystems.NetworkSendSystem + // Animation + AnimationSpriteMatrix stdsystems.AnimationSpriteMatrixSystem + AnimationPlayer stdsystems.AnimationPlayerSystem + // Prerender init + TextureRenderSprite stdsystems.TextureRenderSpriteSystem + TextureRenderSpriteSheet stdsystems.TextureRenderSpriteSheetSystem + TextureRenderMatrix stdsystems.TextureRenderMatrixSystem + // Prerender fill + TextureRenderAnimation stdsystems.TextureRenderAnimationSystem + TextureRenderFlip stdsystems.TextureRenderFlipSystem + TextureRenderPosition stdsystems.TextureRenderPositionSystem + TextureRenderRotation stdsystems.TextureRenderRotationSystem + TextureRenderScale stdsystems.TextureRenderScaleSystem + TextureRenderTint stdsystems.TextureRenderTintSystem + // Render + AssetLib stdsystems.AssetLibSystem + Render stdsystems.RenderSystem +} + +type AnySystemList interface{} +type AnyComponentList interface{} + +func InjectECSToSystems(systemList AnySystemList, world *ecs.World, componentList AnyComponentList) { + reflectedSystemList := reflect.ValueOf(systemList).Elem() + systemsLen := reflectedSystemList.NumField() + + reflectedComponentList := reflect.ValueOf(componentList).Elem() + componentsLen := reflectedComponentList.NumField() + + worldType := reflect.TypeOf(world) + + for i := range systemsLen { + system := reflectedSystemList.Field(i) + systemLen := system.NumField() + + for j := range systemLen { + systemField := system.Field(j) + systemFieldType := systemField.Type() + + if systemFieldType == worldType { + system.Field(j).Set(reflect.ValueOf(world)) + continue + } + + shouldEscape := false + for k := range componentsLen { + component := reflectedComponentList.Field(k).Elem() + componentType := component.Type() + + if systemFieldType.Kind() == reflect.Ptr { + if systemFieldType.Elem() == componentType { + system.Field(j).Set(reflectedComponentList.Field(k)) + shouldEscape = true + } + } else { + if systemFieldType == componentType { + system.Field(j).Set(reflectedComponentList.Field(k)) + shouldEscape = true + } + } + + if shouldEscape { + break + } + } + } + } +} diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index 9a0bb122..9ece27a8 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -16,36 +16,152 @@ package scenes import ( "gomp" - "gomp/examples/new-api/components" - "gomp/examples/new-api/systems" + "gomp/examples/new-api/instances" "gomp/pkg/ecs" "time" ) -func NewMainScene(world *ecs.World, components *components.GameComponents) *MainScene { - return &MainScene{ - playerSystem: systems.NewPlayerSystem(world, components.SpriteMatrix, components.Position, components.Rotation, components.Scale, components.Velocity, components.AnimationPlayer, components.AnimationState, components.Tint, components.Flip), +func NewMainScene() *MainScene { + world := ecs.CreateWorld("Main") + comps := instances.NewComponentList(&world) + sys := instances.NewSystemList(&world, &comps) + + scene := MainScene{ + World: world, + Components: comps, + Systems: sys, } + + return &scene } type MainScene struct { - playerSystem *systems.PlayerSystem + Game *gomp.Game + World ecs.World + Components instances.ComponentList + Systems instances.SystemList } -func (s *MainScene) Init() { - s.playerSystem.Init() +func (s *MainScene) Id() gomp.SceneId { + return MainSceneId } -func (s *MainScene) Destroy() { - s.playerSystem.Destroy() +func (s *MainScene) Init() { + // Network receive + s.Systems.Network.Init() + s.Systems.NetworkReceive.Init() + + // Scenes + s.Systems.Player.Init() + + s.Systems.Velocity.Init() + + // Network patches + s.Systems.NetworkSend.Init() + + // Animation + s.Systems.AnimationSpriteMatrix.Init() + s.Systems.AnimationPlayer.Init() + + // Prerender init + s.Systems.TextureRenderSprite.Init() + s.Systems.TextureRenderSpriteSheet.Init() + s.Systems.TextureRenderMatrix.Init() + + // Prerender fill + s.Systems.TextureRenderAnimation.Init() + s.Systems.TextureRenderFlip.Init() + s.Systems.TextureRenderPosition.Init() + s.Systems.TextureRenderRotation.Init() + s.Systems.TextureRenderScale.Init() + s.Systems.TextureRenderTint.Init() + + // Render + s.Systems.Render.Init() + s.Systems.Debug.Init() + s.Systems.AssetLib.Init() } func (s *MainScene) Update(dt time.Duration) gomp.SceneId { - s.playerSystem.Run(dt) + + // Network receive + s.Systems.Network.Run(dt) + s.Systems.NetworkReceive.Run(dt) + + s.Systems.Player.Run(dt) + + // Network patches + s.Systems.NetworkSend.Run(dt) + + s.Systems.Debug.Run(dt) return MainSceneId } func (s *MainScene) FixedUpdate(dt time.Duration) { + // Network send + s.Systems.NetworkSend.Run(dt) +} + +func (s *MainScene) Render(dt time.Duration) { + s.Systems.Velocity.Run(dt) + + // Animation + s.Systems.AnimationSpriteMatrix.Run(dt) + s.Systems.AnimationPlayer.Run(dt) + + // Prerender init + s.Systems.TextureRenderSprite.Run(dt) + s.Systems.TextureRenderSpriteSheet.Run(dt) + s.Systems.TextureRenderMatrix.Run(dt) + + // Prerender fill + s.Systems.TextureRenderAnimation.Run(dt) + s.Systems.TextureRenderFlip.Run(dt) + s.Systems.TextureRenderPosition.Run(dt) + s.Systems.TextureRenderRotation.Run(dt) + s.Systems.TextureRenderScale.Run(dt) + s.Systems.TextureRenderTint.Run(dt) + + // Render + s.Systems.AssetLib.Run(dt) + shouldContinue := s.Systems.Render.Run(dt) + if !shouldContinue { + s.Game.SetShouldDestroy(true) + return + } +} + +func (s *MainScene) Destroy() { + // Network intents + s.Systems.Network.Destroy() + s.Systems.NetworkReceive.Destroy() + + s.Systems.Player.Destroy() + + // Network patches + s.Systems.NetworkSend.Destroy() + + // Animation + s.Systems.AnimationSpriteMatrix.Destroy() + s.Systems.AnimationPlayer.Destroy() + + // Prerender init + s.Systems.TextureRenderSprite.Destroy() + s.Systems.TextureRenderSpriteSheet.Destroy() + s.Systems.TextureRenderMatrix.Destroy() + + // Prerender fill + s.Systems.TextureRenderAnimation.Destroy() + s.Systems.TextureRenderFlip.Destroy() + s.Systems.TextureRenderPosition.Destroy() + s.Systems.TextureRenderRotation.Destroy() + s.Systems.TextureRenderScale.Destroy() + s.Systems.TextureRenderTint.Destroy() + + // Render + s.Systems.Debug.Destroy() + s.Systems.AssetLib.Destroy() + s.Systems.Render.Destroy() } diff --git a/examples/new-api/scenes/scenes.go b/examples/new-api/scenes/scenes.go deleted file mode 100644 index 8d358e7e..00000000 --- a/examples/new-api/scenes/scenes.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -none :) - -Thank you for your support! -*/ - -package scenes - -import ( - "gomp" - "gomp/examples/new-api/components" - "gomp/pkg/ecs" -) - -func NewSceneSet(world *ecs.World, components *components.GameComponents) map[gomp.SceneId]gomp.AnyScene { - sceneSet := make(map[gomp.SceneId]gomp.AnyScene) - - sceneSet[MainSceneId] = NewMainScene(world, components) - - return sceneSet -} diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index b0999daf..814447ee 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -14,58 +14,36 @@ import ( "time" ) -func NewPlayerSystem( - world *ecs.World, - spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, - positions *stdcomponents.PositionComponentManager, - rotations *stdcomponents.RotationComponentManager, - scales *stdcomponents.ScaleComponentManager, - velocities *stdcomponents.VelocityComponentManager, - animationPlayers *stdcomponents.AnimationPlayerComponentManager, - animationStates *stdcomponents.AnimationStateComponentManager, - tints *stdcomponents.TintComponentManager, - flips *stdcomponents.FlipComponentManager, -) *PlayerSystem { - return &PlayerSystem{ - world: world, - spriteMatrixes: spriteMatrixes, - positions: positions, - rotations: rotations, - scales: scales, - velocities: velocities, - animationPlayers: animationPlayers, - animationStates: animationStates, - tints: tints, - flips: flips, - } +func NewPlayerSystem() PlayerSystem { + return PlayerSystem{} } type PlayerSystem struct { - world *ecs.World - player entities.Player - spriteMatrixes *stdcomponents.SpriteMatrixComponentManager - positions *stdcomponents.PositionComponentManager - rotations *stdcomponents.RotationComponentManager - scales *stdcomponents.ScaleComponentManager - velocities *stdcomponents.VelocityComponentManager - animationPlayers *stdcomponents.AnimationPlayerComponentManager - animationStates *stdcomponents.AnimationStateComponentManager - tints *stdcomponents.TintComponentManager - flips *stdcomponents.FlipComponentManager + World *ecs.World + Player entities.Player + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Scales *stdcomponents.ScaleComponentManager + Velocities *stdcomponents.VelocityComponentManager + AnimationPlayers *stdcomponents.AnimationPlayerComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager + Tints *stdcomponents.TintComponentManager + Flips *stdcomponents.FlipComponentManager } func (s *PlayerSystem) Init() { - s.player = entities.CreatePlayer(s.world, s.spriteMatrixes, s.positions, s.rotations, s.scales, s.velocities, s.animationPlayers, s.animationStates, s.tints, s.flips) - s.player.Position.X = 100 - s.player.Position.Y = 100 + s.Player = entities.CreatePlayer(s.World, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips) + s.Player.Position.X = 100 + s.Player.Position.Y = 100 } func (s *PlayerSystem) Run(dt time.Duration) { - animationState := s.animationStates.Get(s.player.Entity) + animationState := s.AnimationStates.Get(s.Player.Entity) var speed float32 = 300 - s.player.Velocity.X = 0 - s.player.Velocity.Y = 0 + s.Player.Velocity.X = 0 + s.Player.Velocity.Y = 0 if rl.IsKeyDown(rl.KeySpace) { *animationState = entities.PlayerStateJump @@ -73,21 +51,21 @@ func (s *PlayerSystem) Run(dt time.Duration) { *animationState = entities.PlayerStateIdle if rl.IsKeyDown(rl.KeyD) { *animationState = entities.PlayerStateWalk - s.player.Velocity.X = speed - s.player.Flip.X = false + s.Player.Velocity.X = speed + s.Player.Flip.X = false } if rl.IsKeyDown(rl.KeyA) { *animationState = entities.PlayerStateWalk - s.player.Velocity.X = -speed - s.player.Flip.X = true + s.Player.Velocity.X = -speed + s.Player.Flip.X = true } if rl.IsKeyDown(rl.KeyW) { *animationState = entities.PlayerStateWalk - s.player.Velocity.Y = -speed + s.Player.Velocity.Y = -speed } if rl.IsKeyDown(rl.KeyS) { *animationState = entities.PlayerStateWalk - s.player.Velocity.Y = speed + s.Player.Velocity.Y = speed } } } diff --git a/game.go b/game.go new file mode 100644 index 00000000..10acc006 --- /dev/null +++ b/game.go @@ -0,0 +1,109 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package gomp + +import ( + "github.com/negrel/assert" + "reflect" + "time" +) + +func NewGame(scenes ...AnyScene) Game { + sceneSet := make(map[SceneId]AnyScene, len(scenes)) + + for i := range len(scenes) { + id := scenes[i].Id() + + _, exists := sceneSet[id] + assert.False(exists, "Scene with id %d already exists. Duplicating ids?", id) + + sceneSet[id] = scenes[i] + } + + game := Game{ + Scenes: sceneSet, + } + + return game +} + +type Game struct { + Scenes map[SceneId]AnyScene + CurrentSceneId SceneId + + shouldDestroy bool +} + +func (g *Game) Init() { + for _, scene := range g.Scenes { + g.injectToScene(scene) + } + + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + scene.Init() +} + +func (g *Game) Update(dt time.Duration) { + // Scenes + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + g.CurrentSceneId = scene.Update(dt) +} + +func (g *Game) FixedUpdate(dt time.Duration) { + // Scenes + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + scene.FixedUpdate(dt) +} + +func (g *Game) Render(dt time.Duration) { + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + scene.Render(dt) +} + +func (g *Game) Destroy() { + scene, ok := g.Scenes[g.CurrentSceneId] + assert.True(ok, "Scene not found") + scene.Destroy() +} + +func (g *Game) ShouldDestroy() bool { + return g.shouldDestroy +} + +func (g *Game) SetShouldDestroy(value bool) { + g.shouldDestroy = value +} + +func (g *Game) injectToScene(scene AnyScene) { + reflectedScene := reflect.ValueOf(scene).Elem() + sceneLen := reflectedScene.NumField() + + reflectedGame := reflect.ValueOf(g) + gameType := reflect.TypeOf(g) + + for i := 0; i < sceneLen; i++ { + field := reflectedScene.Field(i) + fieldType := field.Type() + + if fieldType == gameType { + reflectedScene.Field(i).Set(reflectedGame) + continue + } + } +} diff --git a/pkg/ecs/ecs.go b/pkg/ecs/ecs.go index acae8aa2..9a906718 100644 --- a/pkg/ecs/ecs.go +++ b/pkg/ecs/ecs.go @@ -17,7 +17,7 @@ const ( PREALLOC_DELETED_ENTITIES uint32 = 1 << 10 ) -func CreateWorld(title string) *World { +func CreateWorld(title string) World { id := generateWorldID() maskSet := NewSparseSet[ComponentBitArray256, Entity]() @@ -33,7 +33,7 @@ func CreateWorld(title string) *World { components: make(map[ComponentID]AnyComponentManagerPtr), } - return &world + return world } func CreateComponentService[T any](id ComponentID) ComponentService[T] { diff --git a/scene.go b/scene.go index 566d76cf..012b9dda 100644 --- a/scene.go +++ b/scene.go @@ -14,15 +14,19 @@ Thank you for your support! package gomp -import "time" +import ( + "time" +) type SceneId uint16 type AnyScene interface { Init() - Destroy() Update(dt time.Duration) SceneId FixedUpdate(dt time.Duration) + Render(dt time.Duration) + Destroy() OnEnter() OnExit() + Id() SceneId } diff --git a/stdsystems/animation-player.go b/stdsystems/animation-player.go index e34cb03f..157e55cb 100644 --- a/stdsystems/animation-player.go +++ b/stdsystems/animation-player.go @@ -15,19 +15,17 @@ import ( "github.com/negrel/assert" ) -type AnimationPlayerSystem struct { - animationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] +func NewAnimationPlayerSystem() AnimationPlayerSystem { + return AnimationPlayerSystem{} } -func NewAnimationPlayerSystem(animationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer]) *AnimationPlayerSystem { - return &AnimationPlayerSystem{ - animationPlayers: animationPlayers, - } +type AnimationPlayerSystem struct { + AnimationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] } func (s *AnimationPlayerSystem) Init() {} func (s *AnimationPlayerSystem) Run(dt time.Duration) { - s.animationPlayers.AllDataParallel(func(animation *stdcomponents.AnimationPlayer) bool { + s.AnimationPlayers.AllDataParallel(func(animation *stdcomponents.AnimationPlayer) bool { animation.ElapsedTime += time.Duration(float32(dt.Microseconds())*animation.Speed) * time.Microsecond assert.True(animation.FrameDuration > 0, fmt.Errorf("frame duration must be greater than 0 (got %v)", animation.FrameDuration)) diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index df99b186..a1052d86 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -12,34 +12,26 @@ import ( "time" ) -func NewAnimationSpriteMatrixSystem(world *ecs.World, - animationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer], - animationStates *ecs.ComponentManager[stdcomponents.AnimationState], - spriteMatrixes *ecs.ComponentManager[stdcomponents.SpriteMatrix]) *AnimationSpriteMatrixSystem { - return &AnimationSpriteMatrixSystem{ - world: world, - animationPlayers: animationPlayers, - animationStates: animationStates, - spriteMatrixes: spriteMatrixes, - } +func NewAnimationSpriteMatrixSystem() AnimationSpriteMatrixSystem { + return AnimationSpriteMatrixSystem{} } type AnimationSpriteMatrixSystem struct { - world *ecs.World - animationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] - animationStates *ecs.ComponentManager[stdcomponents.AnimationState] - spriteMatrixes *ecs.ComponentManager[stdcomponents.SpriteMatrix] + World *ecs.World + AnimationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] + AnimationStates *ecs.ComponentManager[stdcomponents.AnimationState] + SpriteMatrixes *ecs.ComponentManager[stdcomponents.SpriteMatrix] } func (s *AnimationSpriteMatrixSystem) Init() {} func (s *AnimationSpriteMatrixSystem) Run(dt time.Duration) { - s.animationPlayers.AllParallel(func(e ecs.Entity, animationPlayer *stdcomponents.AnimationPlayer) bool { - spriteMatrix := s.spriteMatrixes.Get(e) + s.AnimationPlayers.AllParallel(func(e ecs.Entity, animationPlayer *stdcomponents.AnimationPlayer) bool { + spriteMatrix := s.SpriteMatrixes.Get(e) if spriteMatrix == nil { return true } - animationStatePtr := s.animationStates.Get(e) + animationStatePtr := s.AnimationStates.Get(e) if animationStatePtr == nil { return true } diff --git a/stdsystems/asset-library.go b/stdsystems/asset-library.go index 825ac1b9..f45a716a 100644 --- a/stdsystems/asset-library.go +++ b/stdsystems/asset-library.go @@ -11,8 +11,8 @@ import ( "time" ) -func NewAssetLibSystem(assets []ecs.AnyAssetLibrary) *AssetLibSystem { - return &AssetLibSystem{ +func NewAssetLibSystem(assets []ecs.AnyAssetLibrary) AssetLibSystem { + return AssetLibSystem{ assets: assets, } } diff --git a/stdsystems/debug.go b/stdsystems/debug.go index 9c04145a..c3e09d4a 100644 --- a/stdsystems/debug.go +++ b/stdsystems/debug.go @@ -16,8 +16,8 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" ) -func NewDebugSystem() *DebugSystem { - return &DebugSystem{} +func NewDebugSystem() DebugSystem { + return DebugSystem{} } type DebugSystem struct { diff --git a/stdsystems/network-receive.go b/stdsystems/network-receive.go index a3bf1342..33e6eab3 100644 --- a/stdsystems/network-receive.go +++ b/stdsystems/network-receive.go @@ -16,8 +16,8 @@ package stdsystems import "time" -func NewNetworkReceiveSystem() *NetworkReceiveSystem { - return &NetworkReceiveSystem{} +func NewNetworkReceiveSystem() NetworkReceiveSystem { + return NetworkReceiveSystem{} } type NetworkReceiveSystem struct{} diff --git a/stdsystems/network-send.go b/stdsystems/network-send.go index c522b519..20a5c536 100644 --- a/stdsystems/network-send.go +++ b/stdsystems/network-send.go @@ -22,32 +22,23 @@ import ( "time" ) -func NewNetworkSendSystem(world *ecs.World, - positions *stdcomponents.PositionComponentManager, - rotations *stdcomponents.RotationComponentManager, - mirroreds *stdcomponents.FlipComponentManager, -) *NetworkSendSystem { - return &NetworkSendSystem{ - world: world, - positions: positions, - rotations: rotations, - mirroreds: mirroreds, - } +func NewNetworkSendSystem() NetworkSendSystem { + return NetworkSendSystem{} } type NetworkSendSystem struct { - world *ecs.World - positions *stdcomponents.PositionComponentManager - rotations *stdcomponents.RotationComponentManager - mirroreds *stdcomponents.FlipComponentManager + World *ecs.World + Positions *stdcomponents.PositionComponentManager + Rotations *stdcomponents.RotationComponentManager + Mirroreds *stdcomponents.FlipComponentManager } func (s *NetworkSendSystem) Init() { - s.positions.TrackChanges = true - s.rotations.TrackChanges = true - s.mirroreds.TrackChanges = true + s.Positions.TrackChanges = true + s.Rotations.TrackChanges = true + s.Mirroreds.TrackChanges = true - s.positions.SetEncoder(func(components []stdcomponents.Position) []byte { + s.Positions.SetEncoder(func(components []stdcomponents.Position) []byte { data := make([]byte, 0) for _, component := range components { binary := fmt.Sprintf("%b", component.X) @@ -56,7 +47,7 @@ func (s *NetworkSendSystem) Init() { return data }) - s.rotations.SetEncoder(func(components []stdcomponents.Rotation) []byte { + s.Rotations.SetEncoder(func(components []stdcomponents.Rotation) []byte { data := make([]byte, 0) for _, component := range components { binary := fmt.Sprintf("%b", component.Angle) @@ -65,7 +56,7 @@ func (s *NetworkSendSystem) Init() { return data }) - s.mirroreds.SetEncoder(func(components []stdcomponents.Flip) []byte { + s.Mirroreds.SetEncoder(func(components []stdcomponents.Flip) []byte { data := make([]byte, 0) for _, component := range components { binary := fmt.Sprintf("%b", component.X) diff --git a/stdsystems/network.go b/stdsystems/network.go index 070ab709..4157556e 100644 --- a/stdsystems/network.go +++ b/stdsystems/network.go @@ -28,8 +28,8 @@ const ( Client ) -func NewNetworkSystem() *NetworkSystem { - return &NetworkSystem{} +func NewNetworkSystem() NetworkSystem { + return NetworkSystem{} } type NetworkSystem struct { diff --git a/stdsystems/render.go b/stdsystems/render.go index 61fdf48d..c55273cb 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -14,16 +14,13 @@ import ( "time" ) -type RenderSystem struct { - world *ecs.World - textureRenders *ecs.ComponentManager[stdcomponents.TextureRender] +func NewRenderSystem() RenderSystem { + return RenderSystem{} } -func NewRenderSystem(world *ecs.World, textureRenders *ecs.ComponentManager[stdcomponents.TextureRender]) *RenderSystem { - return &RenderSystem{ - world: world, - textureRenders: textureRenders, - } +type RenderSystem struct { + World *ecs.World + TextureRenders *ecs.ComponentManager[stdcomponents.TextureRender] } func (s *RenderSystem) Init() { @@ -31,16 +28,16 @@ func (s *RenderSystem) Init() { //currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) //rl.SetTargetFPS(currentMonitorRefreshRate) } -func (s *RenderSystem) Run(dt time.Duration) error { +func (s *RenderSystem) Run(dt time.Duration) bool { if rl.WindowShouldClose() { - return fmt.Errorf("window closed") + return false } rl.BeginDrawing() rl.ClearBackground(rl.Black) - s.textureRenders.AllData(func(tr *stdcomponents.TextureRender) bool { + s.TextureRenders.AllData(func(tr *stdcomponents.TextureRender) bool { texture := *tr.Texture rl.DrawTexturePro(texture, tr.Frame, tr.Dest, tr.Origin, tr.Rotation, tr.Tint) return true @@ -48,12 +45,12 @@ func (s *RenderSystem) Run(dt time.Duration) error { // rl.DrawRectangle(0, 0, 120, 120, rl.DarkGray) rl.DrawFPS(10, 10) - rl.DrawText(fmt.Sprintf("%d", s.world.Size()), 10, 30, 20, rl.Red) + rl.DrawText(fmt.Sprintf("%d", s.World.Size()), 10, 30, 20, rl.Red) rl.DrawText(fmt.Sprintf("%s", dt), 10, 50, 20, rl.Red) - rl.DrawText(fmt.Sprintf("%s", s.world.DtFixedUpdate()), 10, 70, 20, rl.Red) + rl.DrawText(fmt.Sprintf("%s", s.World.DtFixedUpdate()), 10, 70, 20, rl.Red) rl.EndDrawing() - return nil + return true } func (s *RenderSystem) Destroy() { diff --git a/stdsystems/texture-render-animation.go b/stdsystems/texture-render-animation.go index ea11dc53..8781aed4 100644 --- a/stdsystems/texture-render-animation.go +++ b/stdsystems/texture-render-animation.go @@ -12,21 +12,25 @@ import ( "time" ) +func NewTextureRenderAnimationSystem() TextureRenderAnimationSystem { + return TextureRenderAnimationSystem{} +} + // TextureRenderAnimationSystem is a system that sets Position of textureRender type TextureRenderAnimationSystem struct { - animations *stdcomponents.AnimationPlayerComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager + Animations *stdcomponents.AnimationPlayerComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderAnimationSystem) Init() {} func (s *TextureRenderAnimationSystem) Run(dt time.Duration) { // Run sprites and spriteRenders - s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true } - animation := s.animations.Get(entity) + animation := s.Animations.Get(entity) if animation == nil { return true } @@ -42,13 +46,3 @@ func (s *TextureRenderAnimationSystem) Run(dt time.Duration) { }) } func (s *TextureRenderAnimationSystem) Destroy() {} - -func NewTextureRenderAnimationSystem( - animations *stdcomponents.AnimationPlayerComponentManager, - textureRenders *stdcomponents.TextureRenderComponentManager, -) *TextureRenderAnimationSystem { - return &TextureRenderAnimationSystem{ - animations: animations, - textureRenders: textureRenders, - } -} diff --git a/stdsystems/texture-render-flip.go b/stdsystems/texture-render-flip.go index 9b730cc6..e3cf88e7 100644 --- a/stdsystems/texture-render-flip.go +++ b/stdsystems/texture-render-flip.go @@ -12,31 +12,25 @@ import ( "time" ) -func NewTextureRenderFlipSystem( - flips *stdcomponents.FlipComponentManager, - textureRenders *stdcomponents.TextureRenderComponentManager, -) *TextureRenderFlipSystem { - return &TextureRenderFlipSystem{ - flips: flips, - textureRenders: textureRenders, - } +func NewTextureRenderFlipSystem() TextureRenderFlipSystem { + return TextureRenderFlipSystem{} } // TextureRenderFlipSystem is a system that sets Scale of textureRender type TextureRenderFlipSystem struct { - flips *stdcomponents.FlipComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager + Flips *stdcomponents.FlipComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderFlipSystem) Init() {} func (s *TextureRenderFlipSystem) Run(dt time.Duration) { // Run sprites and spriteRenders - s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true } - mirrored := s.flips.Get(entity) + mirrored := s.Flips.Get(entity) if mirrored == nil { return true } diff --git a/stdsystems/texture-render-position.go b/stdsystems/texture-render-position.go index f205d1ae..dab1feba 100644 --- a/stdsystems/texture-render-position.go +++ b/stdsystems/texture-render-position.go @@ -12,27 +12,24 @@ import ( "time" ) -func NewTextureRenderPositionSystem(position *stdcomponents.PositionComponentManager, render *ecs.ComponentManager[stdcomponents.TextureRender]) *TextureRenderPositionSystem { - return &TextureRenderPositionSystem{ - positions: position, - textureRenders: render, - } +func NewTextureRenderPositionSystem() TextureRenderPositionSystem { + return TextureRenderPositionSystem{} } // TextureRenderPositionSystem is a system that sets Position of textureRender type TextureRenderPositionSystem struct { - positions *stdcomponents.PositionComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager + Positions *stdcomponents.PositionComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderPositionSystem) Init() {} func (s *TextureRenderPositionSystem) Run(dt time.Duration) { - s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true } - position := s.positions.Get(entity) + position := s.Positions.Get(entity) if position == nil { return true } diff --git a/stdsystems/texture-render-rotation.go b/stdsystems/texture-render-rotation.go index f43b18b3..ae455feb 100644 --- a/stdsystems/texture-render-rotation.go +++ b/stdsystems/texture-render-rotation.go @@ -12,28 +12,25 @@ import ( "time" ) -func NewTextureRenderRotationSystem(rotation *ecs.ComponentManager[stdcomponents.Rotation], render *ecs.ComponentManager[stdcomponents.TextureRender]) *TextureRenderRotationSystem { - return &TextureRenderRotationSystem{ - rotations: rotation, - textureRenders: render, - } +func NewTextureRenderRotationSystem() TextureRenderRotationSystem { + return TextureRenderRotationSystem{} } // TextureRenderRotationSystem is a system that sets Rotation of textureRender type TextureRenderRotationSystem struct { - rotations *stdcomponents.RotationComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager + Rotations *stdcomponents.RotationComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderRotationSystem) Init() {} func (s *TextureRenderRotationSystem) Run(dt time.Duration) { // Run sprites and spriteRenders - s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true } - rotation := s.rotations.Get(entity) + rotation := s.Rotations.Get(entity) if rotation == nil { return true } diff --git a/stdsystems/texture-render-scale.go b/stdsystems/texture-render-scale.go index f4664db5..91fa4335 100644 --- a/stdsystems/texture-render-scale.go +++ b/stdsystems/texture-render-scale.go @@ -12,27 +12,24 @@ import ( "time" ) -func NewTextureRenderScaleSystem(scale *ecs.ComponentManager[stdcomponents.Scale], render *ecs.ComponentManager[stdcomponents.TextureRender]) *TextureRenderScaleSystem { - return &TextureRenderScaleSystem{ - scales: scale, - textureRenders: render, - } +func NewTextureRenderScaleSystem() TextureRenderScaleSystem { + return TextureRenderScaleSystem{} } // TextureRenderScaleSystem is a system that sets Scale of textureRender type TextureRenderScaleSystem struct { - scales *stdcomponents.ScaleComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager + Scales *stdcomponents.ScaleComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderScaleSystem) Init() {} func (s *TextureRenderScaleSystem) Run(dt time.Duration) { - s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true } - scale := s.scales.Get(entity) + scale := s.Scales.Get(entity) if scale == nil { return true } diff --git a/stdsystems/texture-render-sprite.go b/stdsystems/texture-render-sprite.go index d31fe400..5977b0a7 100644 --- a/stdsystems/texture-render-sprite.go +++ b/stdsystems/texture-render-sprite.go @@ -21,26 +21,20 @@ import ( "time" ) -func NewTextureRenderSpriteSystem( - sprites *stdcomponents.SpriteComponentManager, - textureRenders *stdcomponents.TextureRenderComponentManager, -) *TextureRenderSpriteSystem { - return &TextureRenderSpriteSystem{ - sprites: sprites, - textureRenders: textureRenders, - } +func NewTextureRenderSpriteSystem() TextureRenderSpriteSystem { + return TextureRenderSpriteSystem{} } // TextureRenderSpriteSystem is a system that prepares Sprite to be rendered type TextureRenderSpriteSystem struct { - sprites *stdcomponents.SpriteComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager + Sprites *stdcomponents.SpriteComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderSpriteSystem) Init() {} func (s *TextureRenderSpriteSystem) Run(dt time.Duration) { // Run sprites and spriteRenders - s.sprites.AllParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { + s.Sprites.AllParallel(func(entity ecs.Entity, sprite *stdcomponents.Sprite) bool { if sprite == nil { return true } @@ -49,7 +43,7 @@ func (s *TextureRenderSpriteSystem) Run(dt time.Duration) { spriteOrigin := sprite.Origin spriteTint := sprite.Tint - tr := s.textureRenders.Get(entity) + tr := s.TextureRenders.Get(entity) if tr == nil { // Create new spriteRender newRender := stdcomponents.TextureRender{ @@ -65,7 +59,7 @@ func (s *TextureRenderSpriteSystem) Run(dt time.Duration) { ), } - s.textureRenders.Create(entity, newRender) + s.TextureRenders.Create(entity, newRender) } else { // Run spriteRender // tr.Texture = sprite.Texture diff --git a/stdsystems/texture-render-spritematrix.go b/stdsystems/texture-render-spritematrix.go index 9a53cfbd..80774290 100644 --- a/stdsystems/texture-render-spritematrix.go +++ b/stdsystems/texture-render-spritematrix.go @@ -13,41 +13,33 @@ import ( "time" ) -func NewTextureRenderMatrixSystem( - spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, - textureRenders *stdcomponents.TextureRenderComponentManager, - animationStates *stdcomponents.AnimationStateComponentManager, -) *TextureRenderMatrixSystem { - return &TextureRenderMatrixSystem{ - spriteMatrixes: spriteMatrixes, - textureRenders: textureRenders, - animationStates: animationStates, - } +func NewTextureRenderMatrixSystem() TextureRenderMatrixSystem { + return TextureRenderMatrixSystem{} } // TextureRenderMatrixSystem is a system that prepares SpriteSheet to be rendered type TextureRenderMatrixSystem struct { - spriteMatrixes *stdcomponents.SpriteMatrixComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager - animationStates *stdcomponents.AnimationStateComponentManager + SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager + AnimationStates *stdcomponents.AnimationStateComponentManager } func (s *TextureRenderMatrixSystem) Init() {} func (s *TextureRenderMatrixSystem) Run(dt time.Duration) { // Run sprites and spriteRenders - s.spriteMatrixes.AllParallel(func(entity ecs.Entity, spriteMatrix *stdcomponents.SpriteMatrix) bool { + s.SpriteMatrixes.AllParallel(func(entity ecs.Entity, spriteMatrix *stdcomponents.SpriteMatrix) bool { if spriteMatrix == nil { return true } - animationState := s.animationStates.Get(entity) + animationState := s.AnimationStates.Get(entity) if animationState == nil { return true } currentAnimationFrame := spriteMatrix.Animations[*animationState].Frame - tr := s.textureRenders.Get(entity) + tr := s.TextureRenders.Get(entity) if tr == nil { // Create new spriteRender newRender := stdcomponents.TextureRender{ @@ -60,7 +52,7 @@ func (s *TextureRenderMatrixSystem) Run(dt time.Duration) { }, } - s.textureRenders.Create(entity, newRender) + s.TextureRenders.Create(entity, newRender) } else { // Run spriteRender tr.Texture = spriteMatrix.Texture diff --git a/stdsystems/texture-render-spritesheet.go b/stdsystems/texture-render-spritesheet.go index 3cf5a5d8..eb2b8d7a 100644 --- a/stdsystems/texture-render-spritesheet.go +++ b/stdsystems/texture-render-spritesheet.go @@ -13,30 +13,24 @@ import ( "time" ) -func NewTextureRenderSpriteSheetSystem( - spriteSheets *stdcomponents.SpriteSheetComponentManager, - textureRenders *stdcomponents.TextureRenderComponentManager, -) *TextureRenderSpriteSheetSystem { - return &TextureRenderSpriteSheetSystem{ - spriteSheets: spriteSheets, - textureRenders: textureRenders, - } +func NewTextureRenderSpriteSheetSystem() TextureRenderSpriteSheetSystem { + return TextureRenderSpriteSheetSystem{} } // TextureRenderSpriteSheetSystem is a system that prepares SpriteSheet to be rendered type TextureRenderSpriteSheetSystem struct { - spriteSheets *stdcomponents.SpriteSheetComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager + SpriteSheets *stdcomponents.SpriteSheetComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderSpriteSheetSystem) Init() {} func (s *TextureRenderSpriteSheetSystem) Run(dt time.Duration) { - s.spriteSheets.AllParallel(func(entity ecs.Entity, spriteSheet *stdcomponents.SpriteSheet) bool { + s.SpriteSheets.AllParallel(func(entity ecs.Entity, spriteSheet *stdcomponents.SpriteSheet) bool { if spriteSheet == nil { return true } - tr := s.textureRenders.Get(entity) + tr := s.TextureRenders.Get(entity) if tr == nil { // Create new spriteRender newRender := stdcomponents.TextureRender{ @@ -51,7 +45,7 @@ func (s *TextureRenderSpriteSheetSystem) Run(dt time.Duration) { ), } - s.textureRenders.Create(entity, newRender) + s.TextureRenders.Create(entity, newRender) } else { // Run spriteRender tr.Texture = spriteSheet.Texture diff --git a/stdsystems/texture-render-tint.go b/stdsystems/texture-render-tint.go index ba9a9dc6..6089054f 100644 --- a/stdsystems/texture-render-tint.go +++ b/stdsystems/texture-render-tint.go @@ -12,27 +12,24 @@ import ( "time" ) -func NewTextureRenderTintSystem(tint *ecs.ComponentManager[stdcomponents.Tint], render *ecs.ComponentManager[stdcomponents.TextureRender]) *TextureRenderTintSystem { - return &TextureRenderTintSystem{ - tints: tint, - textureRenders: render, - } +func NewTextureRenderTintSystem() TextureRenderTintSystem { + return TextureRenderTintSystem{} } // TextureRenderTintSystem is a system that sets Scale of textureRender type TextureRenderTintSystem struct { - tints *stdcomponents.TintComponentManager - textureRenders *stdcomponents.TextureRenderComponentManager + Tints *stdcomponents.TintComponentManager + TextureRenders *stdcomponents.TextureRenderComponentManager } func (s *TextureRenderTintSystem) Init() {} func (s *TextureRenderTintSystem) Run(dt time.Duration) { - s.textureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { + s.TextureRenders.AllParallel(func(entity ecs.Entity, tr *stdcomponents.TextureRender) bool { if tr == nil { return true } - tint := s.tints.Get(entity) + tint := s.Tints.Get(entity) if tint == nil { return true } diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index f4d0cde2..41f666b9 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -12,25 +12,19 @@ import ( "time" ) -func NewVelocitySystem( - velocities *stdcomponents.VelocityComponentManager, - positions *stdcomponents.PositionComponentManager, -) *VelocitySystem { - return &VelocitySystem{ - velocities: velocities, - positions: positions, - } +func NewVelocitySystem() VelocitySystem { + return VelocitySystem{} } type VelocitySystem struct { - velocities *stdcomponents.VelocityComponentManager - positions *stdcomponents.PositionComponentManager + Velocities *stdcomponents.VelocityComponentManager + Positions *stdcomponents.PositionComponentManager } func (s *VelocitySystem) Init() {} func (s *VelocitySystem) Run(dt time.Duration) { - s.velocities.AllParallel(func(entity ecs.Entity, v *stdcomponents.Velocity) bool { - position := s.positions.Get(entity) + s.Velocities.AllParallel(func(entity ecs.Entity, v *stdcomponents.Velocity) bool { + position := s.Positions.Get(entity) position.X += v.X * float32(dt.Seconds()) position.Y += v.Y * float32(dt.Seconds()) return true From 234100b43bf2c2fcc4f49089fddc9c184c7f10c4 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Tue, 18 Feb 2025 22:33:59 +0300 Subject: [PATCH 009/196] remove pointers to component managers in component list --- desktop-components.go | 26 +++--- examples/new-api/components/hp.go | 2 +- examples/new-api/instances/component-list.go | 28 +++--- examples/new-api/instances/system-list.go | 97 +++++++++----------- examples/new-api/systems/player.go | 5 +- pkg/ecs/component.go | 4 +- stdcomponents/animation-player.go | 2 +- stdcomponents/animation-state.go | 2 +- stdcomponents/flip.go | 2 +- stdcomponents/network.go | 2 +- stdcomponents/position.go | 2 +- stdcomponents/rotation.go | 2 +- stdcomponents/scale.go | 2 +- stdcomponents/sprite-matrix.go | 2 +- stdcomponents/sprite-sheet.go | 2 +- stdcomponents/sprite.go | 2 +- stdcomponents/texture-render.go | 2 +- stdcomponents/tint.go | 2 +- stdcomponents/velocity.go | 2 +- 19 files changed, 88 insertions(+), 100 deletions(-) diff --git a/desktop-components.go b/desktop-components.go index 5db49a89..6f86a0fd 100644 --- a/desktop-components.go +++ b/desktop-components.go @@ -38,17 +38,17 @@ func NewDesktopComponents(world *ecs.World) DesktopComponents { } type DesktopComponents struct { - Position *stdcomponents.PositionComponentManager - Rotation *stdcomponents.RotationComponentManager - Scale *stdcomponents.ScaleComponentManager - Velocity *stdcomponents.VelocityComponentManager - Flip *stdcomponents.FlipComponentManager - Sprite *stdcomponents.SpriteComponentManager - SpriteSheet *stdcomponents.SpriteSheetComponentManager - SpriteMatrix *stdcomponents.SpriteMatrixComponentManager - Tint *stdcomponents.TintComponentManager - AnimationPlayer *stdcomponents.AnimationPlayerComponentManager - AnimationState *stdcomponents.AnimationStateComponentManager - TextureRender *stdcomponents.TextureRenderComponentManager - Network *stdcomponents.NetworkComponentManager + Position stdcomponents.PositionComponentManager + Rotation stdcomponents.RotationComponentManager + Scale stdcomponents.ScaleComponentManager + Velocity stdcomponents.VelocityComponentManager + Flip stdcomponents.FlipComponentManager + Sprite stdcomponents.SpriteComponentManager + SpriteSheet stdcomponents.SpriteSheetComponentManager + SpriteMatrix stdcomponents.SpriteMatrixComponentManager + Tint stdcomponents.TintComponentManager + AnimationPlayer stdcomponents.AnimationPlayerComponentManager + AnimationState stdcomponents.AnimationStateComponentManager + TextureRender stdcomponents.TextureRenderComponentManager + Network stdcomponents.NetworkComponentManager } diff --git a/examples/new-api/components/hp.go b/examples/new-api/components/hp.go index 8cd00baa..cb060b7c 100644 --- a/examples/new-api/components/hp.go +++ b/examples/new-api/components/hp.go @@ -22,6 +22,6 @@ type Health struct { type HealthComponentManager = ecs.ComponentManager[Health] -func NewHealthComponentManager(world *ecs.World) *ecs.ComponentManager[Health] { +func NewHealthComponentManager(world *ecs.World) ecs.ComponentManager[Health] { return ecs.NewComponentManager[Health](world, HEALTH_ID) } diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index 2a63a6cf..ad55ebc6 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -21,20 +21,20 @@ import ( ) type ComponentList struct { - Position *stdcomponents.PositionComponentManager - Rotation *stdcomponents.RotationComponentManager - Scale *stdcomponents.ScaleComponentManager - Velocity *stdcomponents.VelocityComponentManager - Flip *stdcomponents.FlipComponentManager - Sprite *stdcomponents.SpriteComponentManager - SpriteSheet *stdcomponents.SpriteSheetComponentManager - SpriteMatrix *stdcomponents.SpriteMatrixComponentManager - Tint *stdcomponents.TintComponentManager - AnimationPlayer *stdcomponents.AnimationPlayerComponentManager - AnimationState *stdcomponents.AnimationStateComponentManager - TextureRender *stdcomponents.TextureRenderComponentManager - Network *stdcomponents.NetworkComponentManager - Health *components.HealthComponentManager + Position stdcomponents.PositionComponentManager + Rotation stdcomponents.RotationComponentManager + Scale stdcomponents.ScaleComponentManager + Velocity stdcomponents.VelocityComponentManager + Flip stdcomponents.FlipComponentManager + Sprite stdcomponents.SpriteComponentManager + SpriteSheet stdcomponents.SpriteSheetComponentManager + SpriteMatrix stdcomponents.SpriteMatrixComponentManager + Tint stdcomponents.TintComponentManager + AnimationPlayer stdcomponents.AnimationPlayerComponentManager + AnimationState stdcomponents.AnimationStateComponentManager + TextureRender stdcomponents.TextureRenderComponentManager + Network stdcomponents.NetworkComponentManager + Health components.HealthComponentManager } func NewComponentList(world *ecs.World) ComponentList { diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index c1a7e6cf..b8738062 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -24,31 +24,25 @@ import ( func NewSystemList(world *ecs.World, components *ComponentList) SystemList { newSystemList := SystemList{ - Player: systems.NewPlayerSystem(), - Debug: stdsystems.NewDebugSystem(), - - Velocity: stdsystems.NewVelocitySystem(), - - Network: stdsystems.NewNetworkSystem(), - NetworkReceive: stdsystems.NewNetworkReceiveSystem(), - NetworkSend: stdsystems.NewNetworkSendSystem(), - - AnimationSpriteMatrix: stdsystems.NewAnimationSpriteMatrixSystem(), - AnimationPlayer: stdsystems.NewAnimationPlayerSystem(), - + Player: systems.NewPlayerSystem(), + Debug: stdsystems.NewDebugSystem(), + Velocity: stdsystems.NewVelocitySystem(), + Network: stdsystems.NewNetworkSystem(), + NetworkReceive: stdsystems.NewNetworkReceiveSystem(), + NetworkSend: stdsystems.NewNetworkSendSystem(), + AnimationSpriteMatrix: stdsystems.NewAnimationSpriteMatrixSystem(), + AnimationPlayer: stdsystems.NewAnimationPlayerSystem(), TextureRenderSprite: stdsystems.NewTextureRenderSpriteSystem(), TextureRenderSpriteSheet: stdsystems.NewTextureRenderSpriteSheetSystem(), TextureRenderMatrix: stdsystems.NewTextureRenderMatrixSystem(), - - TextureRenderAnimation: stdsystems.NewTextureRenderAnimationSystem(), - TextureRenderFlip: stdsystems.NewTextureRenderFlipSystem(), - TextureRenderPosition: stdsystems.NewTextureRenderPositionSystem(), - TextureRenderRotation: stdsystems.NewTextureRenderRotationSystem(), - TextureRenderScale: stdsystems.NewTextureRenderScaleSystem(), - TextureRenderTint: stdsystems.NewTextureRenderTintSystem(), - - AssetLib: stdsystems.NewAssetLibSystem([]ecs.AnyAssetLibrary{assets.Textures}), - Render: stdsystems.NewRenderSystem(), + TextureRenderAnimation: stdsystems.NewTextureRenderAnimationSystem(), + TextureRenderFlip: stdsystems.NewTextureRenderFlipSystem(), + TextureRenderPosition: stdsystems.NewTextureRenderPositionSystem(), + TextureRenderRotation: stdsystems.NewTextureRenderRotationSystem(), + TextureRenderScale: stdsystems.NewTextureRenderScaleSystem(), + TextureRenderTint: stdsystems.NewTextureRenderTintSystem(), + AssetLib: stdsystems.NewAssetLibSystem([]ecs.AnyAssetLibrary{assets.Textures}), + Render: stdsystems.NewRenderSystem(), } InjectECSToSystems(&newSystemList, world, components) @@ -57,32 +51,25 @@ func NewSystemList(world *ecs.World, components *ComponentList) SystemList { } type SystemList struct { - Player systems.PlayerSystem - Debug stdsystems.DebugSystem - - Velocity stdsystems.VelocitySystem - - // Network - Network stdsystems.NetworkSystem - NetworkReceive stdsystems.NetworkReceiveSystem - NetworkSend stdsystems.NetworkSendSystem - // Animation - AnimationSpriteMatrix stdsystems.AnimationSpriteMatrixSystem - AnimationPlayer stdsystems.AnimationPlayerSystem - // Prerender init + Player systems.PlayerSystem + Debug stdsystems.DebugSystem + Velocity stdsystems.VelocitySystem + Network stdsystems.NetworkSystem + NetworkReceive stdsystems.NetworkReceiveSystem + NetworkSend stdsystems.NetworkSendSystem + AnimationSpriteMatrix stdsystems.AnimationSpriteMatrixSystem + AnimationPlayer stdsystems.AnimationPlayerSystem TextureRenderSprite stdsystems.TextureRenderSpriteSystem TextureRenderSpriteSheet stdsystems.TextureRenderSpriteSheetSystem TextureRenderMatrix stdsystems.TextureRenderMatrixSystem - // Prerender fill - TextureRenderAnimation stdsystems.TextureRenderAnimationSystem - TextureRenderFlip stdsystems.TextureRenderFlipSystem - TextureRenderPosition stdsystems.TextureRenderPositionSystem - TextureRenderRotation stdsystems.TextureRenderRotationSystem - TextureRenderScale stdsystems.TextureRenderScaleSystem - TextureRenderTint stdsystems.TextureRenderTintSystem - // Render - AssetLib stdsystems.AssetLibSystem - Render stdsystems.RenderSystem + TextureRenderAnimation stdsystems.TextureRenderAnimationSystem + TextureRenderFlip stdsystems.TextureRenderFlipSystem + TextureRenderPosition stdsystems.TextureRenderPositionSystem + TextureRenderRotation stdsystems.TextureRenderRotationSystem + TextureRenderScale stdsystems.TextureRenderScaleSystem + TextureRenderTint stdsystems.TextureRenderTintSystem + AssetLib stdsystems.AssetLibSystem + Render stdsystems.RenderSystem } type AnySystemList interface{} @@ -105,26 +92,24 @@ func InjectECSToSystems(systemList AnySystemList, world *ecs.World, componentLis systemField := system.Field(j) systemFieldType := systemField.Type() + if systemFieldType.Kind() != reflect.Ptr { + continue + } + if systemFieldType == worldType { system.Field(j).Set(reflect.ValueOf(world)) continue } + // TODO: refactor to component list indexed map to speed up assignment shouldEscape := false for k := range componentsLen { - component := reflectedComponentList.Field(k).Elem() + component := reflectedComponentList.Field(k) componentType := component.Type() - if systemFieldType.Kind() == reflect.Ptr { - if systemFieldType.Elem() == componentType { - system.Field(j).Set(reflectedComponentList.Field(k)) - shouldEscape = true - } - } else { - if systemFieldType == componentType { - system.Field(j).Set(reflectedComponentList.Field(k)) - shouldEscape = true - } + if systemFieldType.Elem() == componentType { + system.Field(j).Set(component.Addr()) + shouldEscape = true } if shouldEscape { diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index 814447ee..eec34562 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -33,7 +33,10 @@ type PlayerSystem struct { } func (s *PlayerSystem) Init() { - s.Player = entities.CreatePlayer(s.World, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips) + s.Player = entities.CreatePlayer( + s.World, s.SpriteMatrixes, s.Positions, s.Rotations, s.Scales, + s.Velocities, s.AnimationPlayers, s.AnimationStates, s.Tints, s.Flips, + ) s.Player.Position.X = 100 s.Player.Position.Y = 100 } diff --git a/pkg/ecs/component.go b/pkg/ecs/component.go index 58c2c2f8..5bdf5e53 100644 --- a/pkg/ecs/component.go +++ b/pkg/ecs/component.go @@ -82,7 +82,7 @@ func (c *ComponentService[T]) getId() ComponentID { // Service // ================ -func NewComponentManager[T any](world *World, id ComponentID) *ComponentManager[T] { +func NewComponentManager[T any](world *World, id ComponentID) ComponentManager[T] { newManager := ComponentManager[T]{ mx: new(sync.Mutex), @@ -100,7 +100,7 @@ func NewComponentManager[T any](world *World, id ComponentID) *ComponentManager[ deletedEntities: NewPagedArray[Entity](), } - return &newManager + return newManager } type ComponentManager[T any] struct { diff --git a/stdcomponents/animation-player.go b/stdcomponents/animation-player.go index 78017541..b06b7e09 100644 --- a/stdcomponents/animation-player.go +++ b/stdcomponents/animation-player.go @@ -34,6 +34,6 @@ type AnimationPlayer struct { type AnimationPlayerComponentManager = ecs.ComponentManager[AnimationPlayer] -func NewAnimationPlayerComponentManager(world *ecs.World) *AnimationPlayerComponentManager { +func NewAnimationPlayerComponentManager(world *ecs.World) AnimationPlayerComponentManager { return ecs.NewComponentManager[AnimationPlayer](world, ANIMATION_PLAYER_ID) } diff --git a/stdcomponents/animation-state.go b/stdcomponents/animation-state.go index d0006ac4..49a940f4 100644 --- a/stdcomponents/animation-state.go +++ b/stdcomponents/animation-state.go @@ -22,6 +22,6 @@ type AnimationState int32 type AnimationStateComponentManager = ecs.ComponentManager[AnimationState] -func NewAnimationStateComponentManager(world *ecs.World) *AnimationStateComponentManager { +func NewAnimationStateComponentManager(world *ecs.World) AnimationStateComponentManager { return ecs.NewComponentManager[AnimationState](world, ANIMATION_STATE_ID) } diff --git a/stdcomponents/flip.go b/stdcomponents/flip.go index 4ab2d16e..7edf28db 100644 --- a/stdcomponents/flip.go +++ b/stdcomponents/flip.go @@ -22,6 +22,6 @@ type Flip struct { type FlipComponentManager = ecs.ComponentManager[Flip] -func NewFlipComponentManager(world *ecs.World) *FlipComponentManager { +func NewFlipComponentManager(world *ecs.World) FlipComponentManager { return ecs.NewComponentManager[Flip](world, FLIP_ID) } diff --git a/stdcomponents/network.go b/stdcomponents/network.go index d16e531f..9a7fc5bf 100644 --- a/stdcomponents/network.go +++ b/stdcomponents/network.go @@ -25,6 +25,6 @@ type Network struct { type NetworkComponentManager = ecs.ComponentManager[Network] -func NewNetworkComponentManager(world *ecs.World) *NetworkComponentManager { +func NewNetworkComponentManager(world *ecs.World) NetworkComponentManager { return ecs.NewComponentManager[Network](world, NETWORK_ID) } diff --git a/stdcomponents/position.go b/stdcomponents/position.go index c519e05a..24b94b81 100644 --- a/stdcomponents/position.go +++ b/stdcomponents/position.go @@ -22,6 +22,6 @@ type Position struct { type PositionComponentManager = ecs.ComponentManager[Position] -func NewPositionComponentManager(world *ecs.World) *PositionComponentManager { +func NewPositionComponentManager(world *ecs.World) PositionComponentManager { return ecs.NewComponentManager[Position](world, POSITION_ID) } diff --git a/stdcomponents/rotation.go b/stdcomponents/rotation.go index e3447013..8e707164 100644 --- a/stdcomponents/rotation.go +++ b/stdcomponents/rotation.go @@ -22,6 +22,6 @@ type Rotation struct { type RotationComponentManager = ecs.ComponentManager[Rotation] -func NewRotationComponentManager(world *ecs.World) *RotationComponentManager { +func NewRotationComponentManager(world *ecs.World) RotationComponentManager { return ecs.NewComponentManager[Rotation](world, ROTATION_ID) } diff --git a/stdcomponents/scale.go b/stdcomponents/scale.go index d4efafeb..28cb7b1a 100644 --- a/stdcomponents/scale.go +++ b/stdcomponents/scale.go @@ -22,6 +22,6 @@ type Scale struct { type ScaleComponentManager = ecs.ComponentManager[Scale] -func NewScaleComponentManager(world *ecs.World) *ScaleComponentManager { +func NewScaleComponentManager(world *ecs.World) ScaleComponentManager { return ecs.NewComponentManager[Scale](world, SCALE_ID) } diff --git a/stdcomponents/sprite-matrix.go b/stdcomponents/sprite-matrix.go index 808d9b10..d83d6044 100644 --- a/stdcomponents/sprite-matrix.go +++ b/stdcomponents/sprite-matrix.go @@ -36,6 +36,6 @@ type SpriteMatrix struct { type SpriteMatrixComponentManager = ecs.ComponentManager[SpriteMatrix] -func NewSpriteMatrixComponentManager(world *ecs.World) *SpriteMatrixComponentManager { +func NewSpriteMatrixComponentManager(world *ecs.World) SpriteMatrixComponentManager { return ecs.NewComponentManager[SpriteMatrix](world, SPRITE_MATRIX_ID) } diff --git a/stdcomponents/sprite-sheet.go b/stdcomponents/sprite-sheet.go index c9e4eb64..87363023 100644 --- a/stdcomponents/sprite-sheet.go +++ b/stdcomponents/sprite-sheet.go @@ -30,6 +30,6 @@ type SpriteSheet struct { type SpriteSheetComponentManager = ecs.ComponentManager[SpriteSheet] -func NewSpriteSheetComponentManager(world *ecs.World) *SpriteSheetComponentManager { +func NewSpriteSheetComponentManager(world *ecs.World) SpriteSheetComponentManager { return ecs.NewComponentManager[SpriteSheet](world, SPRITE_SHEET_ID) } diff --git a/stdcomponents/sprite.go b/stdcomponents/sprite.go index 6d43f3c2..70250a29 100644 --- a/stdcomponents/sprite.go +++ b/stdcomponents/sprite.go @@ -29,6 +29,6 @@ type Sprite struct { type SpriteComponentManager = ecs.ComponentManager[Sprite] -func NewSpriteComponentManager(world *ecs.World) *SpriteComponentManager { +func NewSpriteComponentManager(world *ecs.World) SpriteComponentManager { return ecs.NewComponentManager[Sprite](world, SPRITE_ID) } diff --git a/stdcomponents/texture-render.go b/stdcomponents/texture-render.go index 34d69d00..98b926b7 100644 --- a/stdcomponents/texture-render.go +++ b/stdcomponents/texture-render.go @@ -31,6 +31,6 @@ type TextureRender struct { type TextureRenderComponentManager = ecs.ComponentManager[TextureRender] -func NewTextureRenderComponentManager(world *ecs.World) *TextureRenderComponentManager { +func NewTextureRenderComponentManager(world *ecs.World) TextureRenderComponentManager { return ecs.NewComponentManager[TextureRender](world, TEXTURE_RENDER_ID) } diff --git a/stdcomponents/tint.go b/stdcomponents/tint.go index 6d4349ca..4703cb05 100644 --- a/stdcomponents/tint.go +++ b/stdcomponents/tint.go @@ -23,6 +23,6 @@ type Tint = color.RGBA type TintComponentManager = ecs.ComponentManager[Tint] -func NewTintComponentManager(world *ecs.World) *TintComponentManager { +func NewTintComponentManager(world *ecs.World) TintComponentManager { return ecs.NewComponentManager[Tint](world, TINT_ID) } diff --git a/stdcomponents/velocity.go b/stdcomponents/velocity.go index fd8843ce..c99dd48a 100644 --- a/stdcomponents/velocity.go +++ b/stdcomponents/velocity.go @@ -22,6 +22,6 @@ type Velocity struct { type VelocityComponentManager = ecs.ComponentManager[Velocity] -func NewVelocityComponentManager(world *ecs.World) *VelocityComponentManager { +func NewVelocityComponentManager(world *ecs.World) VelocityComponentManager { return ecs.NewComponentManager[Velocity](world, VELOCITY_ID) } From a81ce797deb6015111c05c67897d82dbdf9bf930 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 19 Feb 2025 12:46:47 +0300 Subject: [PATCH 010/196] update go version and deps --- go.mod | 37 ++++++++++++++--------------- go.sum | 74 ++++++++++++++++++++++++++++------------------------------ 2 files changed, 54 insertions(+), 57 deletions(-) diff --git a/go.mod b/go.mod index ac59a682..2d596b51 100644 --- a/go.mod +++ b/go.mod @@ -1,30 +1,29 @@ module gomp -go 1.23.5 +go 1.24.0 replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go v1.1.7 require ( github.com/coder/websocket v1.8.12 - github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b - github.com/hajimehoshi/ebiten/v2 v2.7.10 - github.com/jakecoffman/cp/v2 v2.0.2 - github.com/labstack/echo-contrib v0.17.1 + github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5 + github.com/hajimehoshi/ebiten/v2 v2.8.6 + github.com/jakecoffman/cp/v2 v2.1.0 + github.com/labstack/echo-contrib v0.17.2 github.com/labstack/gommon v0.4.2 github.com/negrel/assert v0.5.0 github.com/quasilyte/ebitengine-input v0.9.1 github.com/quic-go/quic-go v0.49.0 github.com/sevenNt/echo-pprof v0.1.1-0.20230131020615-4dd36891e14b github.com/stretchr/testify v1.10.0 - github.com/yohamta/donburi v1.15.4 - golang.org/x/time v0.5.0 + github.com/yohamta/donburi v1.15.7 + golang.org/x/time v0.10.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -37,25 +36,25 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect - golang.org/x/mod v0.18.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/tools v0.25.0 // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( - github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 // indirect + github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect github.com/ebitengine/hideconsole v1.0.0 // indirect - github.com/ebitengine/purego v0.7.1 // indirect + github.com/ebitengine/purego v0.8.0 // indirect github.com/gorilla/sessions v1.4.0 github.com/gorilla/websocket v1.5.3 github.com/jezek/xgb v1.1.1 // indirect - github.com/labstack/echo/v4 v4.12.0 + github.com/labstack/echo/v4 v4.13.3 github.com/mattn/go-isatty v0.0.20 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/text v0.17.0 - google.golang.org/protobuf v1.34.1 + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 + google.golang.org/protobuf v1.36.1 ) diff --git a/go.sum b/go.sum index f14a7756..4dff1431 100644 --- a/go.sum +++ b/go.sum @@ -23,18 +23,18 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU= -github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0/hb6Tq3WkmutWa0ZLhNn/6fc6XZpM7tM= +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= -github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= +github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b h1:JJfspevP3YOXcSKVABizYOv++yMpTJIdPUtoDzF/RWw= -github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= +github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5 h1:k8ZAxLgb/p5TvCi5VHFHM8JdnjwShNK4A0bLIwbktAU= +github.com/gen2brain/raylib-go/raylib v0.0.0-20250215042252-db8e47f0e5c5/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -43,8 +43,6 @@ github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -78,11 +76,11 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/hajimehoshi/ebiten/v2 v2.7.10 h1:fsVukQdPDUlalSSpFkuszTy0cK2DL0fxFoSnTVdlmAM= -github.com/hajimehoshi/ebiten/v2 v2.7.10/go.mod h1:Ulbq5xDmdx47P24EJ+Mb31Zps7vQq+guieG9mghQUaA= +github.com/hajimehoshi/ebiten/v2 v2.8.6 h1:Dkd/sYI0TYyZRCE7GVxV59XC+WCi2BbGAbIBjXeVC1U= +github.com/hajimehoshi/ebiten/v2 v2.8.6/go.mod h1:cCQ3np7rdmaJa1ZnvslraVlpxNb3wCjEnAP1LHNyXNA= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jakecoffman/cp/v2 v2.0.2 h1:HN+youpOhd8xgWYw5amqiJFLoreAIB/uI/EEzZohLjA= -github.com/jakecoffman/cp/v2 v2.0.2/go.mod h1:Q0hFU7Kk6PMw4dwgFtvBC6O4KTm7ewiLuHrXtHMicyU= +github.com/jakecoffman/cp/v2 v2.1.0 h1:s0almZ7zDZs9JY35ciUgCoVKTMmdPkokF1dxHg226Wo= +github.com/jakecoffman/cp/v2 v2.1.0/go.mod h1:Q0hFU7Kk6PMw4dwgFtvBC6O4KTm7ewiLuHrXtHMicyU= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= @@ -94,10 +92,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/labstack/echo-contrib v0.17.1 h1:7I/he7ylVKsDUieaGRZ9XxxTYOjfQwVzHzUYrNykfCU= -github.com/labstack/echo-contrib v0.17.1/go.mod h1:SnsCZtwHBAZm5uBSAtQtXQHI3wqEA73hvTn0bYMKnZA= -github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= -github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/echo-contrib v0.17.2 h1:K1zivqmtcC70X9VdBFdLomjPDEVHlrcAObqmuFj1c6w= +github.com/labstack/echo-contrib v0.17.2/go.mod h1:NeDh3PX7j/u+jR4iuDt1zHmWZSCz9c/p9mxXcDpyS8E= +github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= +github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= @@ -175,8 +173,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/yohamta/donburi v1.15.4 h1:R3nWrYgnhyDBn3NjtEjHMfxBdtq2m1/IiEjgdDW9vqc= -github.com/yohamta/donburi v1.15.4/go.mod h1:FdjU9hpwAsAs1qRvqsSTJimPJ0dipvdnr9hMJXYc1Rk= +github.com/yohamta/donburi v1.15.7 h1:so/vHf1L133d0SFVrCUzMMueh2ko39wRkrcpNLdzvz8= +github.com/yohamta/donburi v1.15.7/go.mod h1:FdjU9hpwAsAs1qRvqsSTJimPJ0dipvdnr9hMJXYc1Rk= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= @@ -185,18 +183,18 @@ golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= +golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -205,8 +203,8 @@ golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -216,8 +214,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -226,22 +224,22 @@ golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -258,8 +256,8 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 36292ac66e1a1057f3f89c25b5795e2fac87b301 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 19 Feb 2025 12:49:51 +0300 Subject: [PATCH 011/196] update github master_workflow.yml --- .github/workflows/master_workflow.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/master_workflow.yml b/.github/workflows/master_workflow.yml index 5b9a2596..8b4a3a5e 100644 --- a/.github/workflows/master_workflow.yml +++ b/.github/workflows/master_workflow.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: os: [ 'ubuntu-latest' ] - go-version: [ '1.23.5' ] + go-version: [ '1.24.0' ] uses: ./.github/workflows/go_ci.yml with: go-version: ${{ matrix.go-version }} @@ -21,4 +21,4 @@ jobs: security-analysis: uses: ./.github/workflows/gosec_security_check.yml with: - go-version: '1.23.5' \ No newline at end of file + go-version: '1.24.0' \ No newline at end of file From 4e22a6bd8470f5952cbadc1feff64802b7fdcd56 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Wed, 19 Feb 2025 16:14:51 +0300 Subject: [PATCH 012/196] fix memory issues --- engine.go | 9 +++------ stdsystems/velocity.go | 5 +++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/engine.go b/engine.go index 51e60127..822cf958 100644 --- a/engine.go +++ b/engine.go @@ -62,15 +62,13 @@ func (e *Engine) Run(tickrate uint, framerate uint) { for !e.Game.ShouldDestroy() { // Update - dt := time.Since(e.lastUpdateAt) - e.Game.Update(dt) + e.Game.Update(time.Since(e.lastUpdateAt)) e.lastUpdateAt = time.Now() // Fixed Update select { case <-fixedUpdTicker.C: - dt := time.Since(e.lastFixedUpdateAt) - e.Game.FixedUpdate(dt) + e.Game.FixedUpdate(time.Since(e.lastFixedUpdateAt)) e.lastFixedUpdateAt = time.Now() default: break @@ -79,8 +77,7 @@ func (e *Engine) Run(tickrate uint, framerate uint) { // Render select { case <-renderTicker.C: - dt := time.Since(e.lastRenderAt) - e.Game.Render(dt) + e.Game.Render(time.Since(e.lastRenderAt)) e.lastRenderAt = time.Now() default: break diff --git a/stdsystems/velocity.go b/stdsystems/velocity.go index 41f666b9..3f9aee81 100644 --- a/stdsystems/velocity.go +++ b/stdsystems/velocity.go @@ -23,10 +23,11 @@ type VelocitySystem struct { func (s *VelocitySystem) Init() {} func (s *VelocitySystem) Run(dt time.Duration) { + dtSec := float32(dt.Seconds()) s.Velocities.AllParallel(func(entity ecs.Entity, v *stdcomponents.Velocity) bool { position := s.Positions.Get(entity) - position.X += v.X * float32(dt.Seconds()) - position.Y += v.Y * float32(dt.Seconds()) + position.X += v.X * dtSec + position.Y += v.Y * dtSec return true }) } From 6034af378ca06d19658ef829ab9c108179889c19 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Thu, 20 Feb 2025 14:40:01 +0300 Subject: [PATCH 013/196] refactor huge renaming breaking changes --- pkg/ecs/asset-library.go => asset-library.go | 2 +- desktop-components.go | 2 +- examples/ebiten-ecs-370k/camera-system.go | 8 +- examples/ebiten-ecs-370k/color-system.go | 6 +- examples/ebiten-ecs-370k/destroy-system.go | 8 +- examples/ebiten-ecs-370k/game.go | 4 +- examples/ebiten-ecs-370k/hp-system.go | 6 +- examples/ebiten-ecs-370k/spawn-system.go | 8 +- examples/new-api/assets/assets.go | 4 +- examples/new-api/components/hp.go | 4 +- examples/new-api/entities/player.go | 4 +- examples/new-api/instances/component-list.go | 2 +- examples/new-api/instances/system-list.go | 9 +- examples/new-api/scenes/main-scene.go | 140 +++--- examples/new-api/systems/hp.go | 10 +- examples/new-api/systems/player.go | 2 +- examples/raylib-ecs/assets/assets.go | 4 +- examples/raylib-ecs/entities/player.go | 4 +- examples/raylib-ecs/main.go | 2 +- .../raylib-ecs/systems/animation-player.go | 8 +- .../systems/animation-spritematrix.go | 8 +- examples/raylib-ecs/systems/asset-library.go | 11 +- examples/raylib-ecs/systems/color.go | 8 +- examples/raylib-ecs/systems/debug.go | 8 +- examples/raylib-ecs/systems/example.go | 8 +- examples/raylib-ecs/systems/hp.go | 10 +- examples/raylib-ecs/systems/init.go | 8 +- .../raylib-ecs/systems/network-receive.go | 8 +- examples/raylib-ecs/systems/network-send.go | 8 +- examples/raylib-ecs/systems/network.go | 8 +- examples/raylib-ecs/systems/player.go | 8 +- examples/raylib-ecs/systems/render.go | 8 +- examples/raylib-ecs/systems/spawn.go | 10 +- examples/raylib-ecs/systems/systems-engine.go | 3 +- .../systems/texture-render-animation.go | 8 +- .../systems/texture-render-mirrored.go | 8 +- .../systems/texture-render-position.go | 8 +- .../systems/texture-render-rotation.go | 8 +- .../systems/texture-render-scale.go | 8 +- .../systems/texture-render-sprite.go | 8 +- .../systems/texture-render-spritematrix.go | 8 +- .../systems/texture-render-spritesheet.go | 8 +- .../raylib-ecs/systems/texture-render-tint.go | 8 +- pkg/ecs/component-manager.go | 370 +++++++++++----- ...nent_test.go => component-manager_test.go} | 24 +- pkg/ecs/component-service.go | 64 +++ pkg/ecs/component.go | 411 ------------------ pkg/ecs/ecs.go | 32 +- pkg/ecs/entity-manager.go | 163 +++++++ pkg/ecs/example.go | 30 +- pkg/ecs/example_test.go | 2 +- pkg/ecs/sparse-set.go | 4 +- pkg/ecs/system-builder.go | 35 -- pkg/ecs/system.go | 139 +----- pkg/ecs/world.go | 294 ------------- stdcomponents/animation-player.go | 2 +- stdcomponents/animation-state.go | 2 +- stdcomponents/flip.go | 2 +- stdcomponents/network.go | 2 +- stdcomponents/position.go | 2 +- stdcomponents/rotation.go | 2 +- stdcomponents/scale.go | 2 +- stdcomponents/sprite-matrix.go | 2 +- stdcomponents/sprite-sheet.go | 2 +- stdcomponents/sprite.go | 2 +- stdcomponents/texture-render.go | 2 +- stdcomponents/tint.go | 2 +- stdcomponents/velocity.go | 2 +- stdsystems/animation-spritematrix.go | 2 +- stdsystems/asset-library.go | 6 +- stdsystems/network-send.go | 2 +- stdsystems/render.go | 3 +- 72 files changed, 761 insertions(+), 1269 deletions(-) rename pkg/ecs/asset-library.go => asset-library.go (99%) rename pkg/ecs/{component_test.go => component-manager_test.go} (85%) create mode 100644 pkg/ecs/component-service.go delete mode 100644 pkg/ecs/component.go create mode 100644 pkg/ecs/entity-manager.go delete mode 100644 pkg/ecs/system-builder.go delete mode 100644 pkg/ecs/world.go diff --git a/pkg/ecs/asset-library.go b/asset-library.go similarity index 99% rename from pkg/ecs/asset-library.go rename to asset-library.go index 1fb66553..e162e749 100644 --- a/pkg/ecs/asset-library.go +++ b/asset-library.go @@ -4,7 +4,7 @@ Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ecs +package gomp import ( "fmt" diff --git a/desktop-components.go b/desktop-components.go index 6f86a0fd..f4a4f25b 100644 --- a/desktop-components.go +++ b/desktop-components.go @@ -19,7 +19,7 @@ import ( "gomp/stdcomponents" ) -func NewDesktopComponents(world *ecs.World) DesktopComponents { +func NewDesktopComponents(world *ecs.EntityManager) DesktopComponents { return DesktopComponents{ Position: stdcomponents.NewPositionComponentManager(world), Rotation: stdcomponents.NewRotationComponentManager(world), diff --git a/examples/ebiten-ecs-370k/camera-system.go b/examples/ebiten-ecs-370k/camera-system.go index 2370ab1f..af71dd0d 100644 --- a/examples/ebiten-ecs-370k/camera-system.go +++ b/examples/ebiten-ecs-370k/camera-system.go @@ -33,7 +33,7 @@ type cameraSystem struct { p *message.Printer } -func (s *cameraSystem) Init(world *ecs2.World) { +func (s *cameraSystem) Init(world *ecs2.EntityManager) { s.transformComponent = transformComponentType.GetManager(world) s.healthComponent = healthComponentType.GetManager(world) s.colorComponent = colorComponentType.GetManager(world) @@ -43,7 +43,7 @@ func (s *cameraSystem) Init(world *ecs2.World) { s.p = message.NewPrinter(language.Russian) - newcamera := world.CreateEntity("camera") + newcamera := world.Create() s.cameraComponent.Create(newcamera, camera{ mainLayer: cameraLayer{ image: ebiten.NewImage(width, height), @@ -59,7 +59,7 @@ func (s *cameraSystem) Init(world *ecs2.World) { s.debugInfo = make([]string, 0) } -func (s *cameraSystem) Run(world *ecs2.World) { +func (s *cameraSystem) Run(world *ecs2.EntityManager) { _, dy := ebiten.Wheel() draw.Draw(s.buffer, s.buffer.Bounds(), &image.Uniform{color.Transparent}, image.Point{}, draw.Src) @@ -111,4 +111,4 @@ func (s *cameraSystem) Run(world *ecs2.World) { ebitenutil.DebugPrint(mainCamera.debugLayer.image, strings.Join(s.debugInfo, "\n")) s.debugInfo = s.debugInfo[:0] } -func (s *cameraSystem) Destroy(world *ecs2.World) {} +func (s *cameraSystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs-370k/color-system.go b/examples/ebiten-ecs-370k/color-system.go index d0a58d00..e350e361 100644 --- a/examples/ebiten-ecs-370k/color-system.go +++ b/examples/ebiten-ecs-370k/color-system.go @@ -20,7 +20,7 @@ type colorSystem struct { baseColor color.RGBA } -func (s *colorSystem) Init(world *ecs2.World) { +func (s *colorSystem) Init(world *ecs2.EntityManager) { s.transformComponent = transformComponentType.GetManager(world) s.healthComponent = healthComponentType.GetManager(world) s.colorComponent = colorComponentType.GetManager(world) @@ -28,7 +28,7 @@ func (s *colorSystem) Init(world *ecs2.World) { s.baseColor = color.RGBA{25, 220, 200, 255} } -func (s *colorSystem) Run(world *ecs2.World) { +func (s *colorSystem) Run(world *ecs2.EntityManager) { s.colorComponent.AllParallel(func(ei ecs2.Entity, c *color.RGBA) bool { health := s.healthComponent.Get(ei) if health == nil { @@ -45,4 +45,4 @@ func (s *colorSystem) Run(world *ecs2.World) { return true }) } -func (s *colorSystem) Destroy(world *ecs2.World) {} +func (s *colorSystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs-370k/destroy-system.go b/examples/ebiten-ecs-370k/destroy-system.go index a0c5e922..b84cb587 100644 --- a/examples/ebiten-ecs-370k/destroy-system.go +++ b/examples/ebiten-ecs-370k/destroy-system.go @@ -21,7 +21,7 @@ type destroySystem struct { n int } -func (s *destroySystem) Init(world *ecs2.World) { +func (s *destroySystem) Init(world *ecs2.EntityManager) { s.transformComponent = transformComponentType.GetManager(world) s.healthComponent = healthComponentType.GetManager(world) s.colorComponent = colorComponentType.GetManager(world) @@ -29,13 +29,13 @@ func (s *destroySystem) Init(world *ecs2.World) { s.destroyComponent = destroyComponentType.GetManager(world) } -func (s *destroySystem) Run(world *ecs2.World) { +func (s *destroySystem) Run(world *ecs2.EntityManager) { s.n = 0 s.destroyComponent.All(func(e ecs2.Entity, h *empty) bool { - world.DestroyEntity(e) + world.Delete(e) entityCount-- return true }) } -func (s *destroySystem) Destroy(world *ecs2.World) {} +func (s *destroySystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs-370k/game.go b/examples/ebiten-ecs-370k/game.go index ef2306fe..0c782cac 100644 --- a/examples/ebiten-ecs-370k/game.go +++ b/examples/ebiten-ecs-370k/game.go @@ -12,13 +12,13 @@ import ( ) type game struct { - world *ecs2.World + world *ecs2.EntityManager cameraComponents *ecs2.ComponentManager[camera] op *ebiten.DrawImageOptions } func newGame() game { - world := ecs2.CreateWorld("1 mil pixel") + world := ecs2.NewEntityManager("1 mil pixel") world.RegisterComponentServices( &destroyComponentType, diff --git a/examples/ebiten-ecs-370k/hp-system.go b/examples/ebiten-ecs-370k/hp-system.go index 3004d1cf..197baa80 100644 --- a/examples/ebiten-ecs-370k/hp-system.go +++ b/examples/ebiten-ecs-370k/hp-system.go @@ -19,14 +19,14 @@ type hpSystem struct { destroyComponent *ecs2.ComponentManager[empty] } -func (s *hpSystem) Init(world *ecs2.World) { +func (s *hpSystem) Init(world *ecs2.EntityManager) { s.transformComponent = transformComponentType.GetManager(world) s.healthComponent = healthComponentType.GetManager(world) s.colorComponent = colorComponentType.GetManager(world) s.movementComponent = movementComponentType.GetManager(world) s.destroyComponent = destroyComponentType.GetManager(world) } -func (s *hpSystem) Run(world *ecs2.World) { +func (s *hpSystem) Run(world *ecs2.EntityManager) { s.healthComponent.AllParallel(func(entity ecs2.Entity, h *health) bool { h.hp-- @@ -37,4 +37,4 @@ func (s *hpSystem) Run(world *ecs2.World) { return true }) } -func (s *hpSystem) Destroy(world *ecs2.World) {} +func (s *hpSystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs-370k/spawn-system.go b/examples/ebiten-ecs-370k/spawn-system.go index 45c41597..debcb7c2 100644 --- a/examples/ebiten-ecs-370k/spawn-system.go +++ b/examples/ebiten-ecs-370k/spawn-system.go @@ -35,17 +35,17 @@ const ( var entityCount = 0 var pprofEnabled = false -func (s *spawnSystem) Init(world *ecs2.World) { +func (s *spawnSystem) Init(world *ecs2.EntityManager) { s.transformComponent = transformComponentType.GetManager(world) s.healthComponent = healthComponentType.GetManager(world) s.colorComponent = colorComponentType.GetManager(world) s.movementComponent = movementComponentType.GetManager(world) } -func (s *spawnSystem) Run(world *ecs2.World) { +func (s *spawnSystem) Run(world *ecs2.EntityManager) { if ebiten.IsKeyPressed(ebiten.KeySpace) { for range rand.Intn(1000) { - newCreature := world.CreateEntity("Creature") + newCreature := world.Create("Creature") t := transform{ x: rand.Int31n(1000), @@ -102,4 +102,4 @@ func (s *spawnSystem) Run(world *ecs2.World) { } } } -func (s *spawnSystem) Destroy(world *ecs2.World) {} +func (s *spawnSystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/new-api/assets/assets.go b/examples/new-api/assets/assets.go index ce4046ed..efe64017 100644 --- a/examples/new-api/assets/assets.go +++ b/examples/new-api/assets/assets.go @@ -9,10 +9,10 @@ package assets import ( rl "github.com/gen2brain/raylib-go/raylib" "github.com/negrel/assert" - "gomp/pkg/ecs" + "gomp" ) -var Textures = ecs.CreateAssetLibrary( +var Textures = gomp.CreateAssetLibrary( func(path string) rl.Texture2D { assert.True(rl.IsWindowReady(), "Window is not initialized") return rl.LoadTexture(path) diff --git a/examples/new-api/components/hp.go b/examples/new-api/components/hp.go index cb060b7c..b3d4cfbc 100644 --- a/examples/new-api/components/hp.go +++ b/examples/new-api/components/hp.go @@ -22,6 +22,6 @@ type Health struct { type HealthComponentManager = ecs.ComponentManager[Health] -func NewHealthComponentManager(world *ecs.World) ecs.ComponentManager[Health] { - return ecs.NewComponentManager[Health](world, HEALTH_ID) +func NewHealthComponentManager(entityManager *ecs.EntityManager) HealthComponentManager { + return ecs.NewComponentManager[Health](entityManager, HEALTH_ID) } diff --git a/examples/new-api/entities/player.go b/examples/new-api/entities/player.go index 36e0eb1b..d77f69c1 100644 --- a/examples/new-api/entities/player.go +++ b/examples/new-api/entities/player.go @@ -66,7 +66,7 @@ var playerSpriteMatrix = stdcomponents.SpriteMatrix{ } func CreatePlayer( - world *ecs.World, + world *ecs.EntityManager, spriteMatrixes *stdcomponents.SpriteMatrixComponentManager, positions *stdcomponents.PositionComponentManager, rotations *stdcomponents.RotationComponentManager, @@ -79,7 +79,7 @@ func CreatePlayer( ) (player Player) { // Creating new player - entity := world.CreateEntity("player") + entity := world.Create() player.Entity = entity // Adding position component diff --git a/examples/new-api/instances/component-list.go b/examples/new-api/instances/component-list.go index ad55ebc6..0aa93e21 100644 --- a/examples/new-api/instances/component-list.go +++ b/examples/new-api/instances/component-list.go @@ -37,7 +37,7 @@ type ComponentList struct { Health components.HealthComponentManager } -func NewComponentList(world *ecs.World) ComponentList { +func NewComponentList(world *ecs.EntityManager) ComponentList { return ComponentList{ Position: stdcomponents.NewPositionComponentManager(world), Rotation: stdcomponents.NewRotationComponentManager(world), diff --git a/examples/new-api/instances/system-list.go b/examples/new-api/instances/system-list.go index b8738062..244ddb5c 100644 --- a/examples/new-api/instances/system-list.go +++ b/examples/new-api/instances/system-list.go @@ -15,6 +15,7 @@ Thank you for your support! package instances import ( + "gomp" "gomp/examples/new-api/assets" "gomp/examples/new-api/systems" "gomp/pkg/ecs" @@ -22,7 +23,7 @@ import ( "reflect" ) -func NewSystemList(world *ecs.World, components *ComponentList) SystemList { +func NewSystemList(entityManager *ecs.EntityManager, componentList *ComponentList) SystemList { newSystemList := SystemList{ Player: systems.NewPlayerSystem(), Debug: stdsystems.NewDebugSystem(), @@ -41,11 +42,11 @@ func NewSystemList(world *ecs.World, components *ComponentList) SystemList { TextureRenderRotation: stdsystems.NewTextureRenderRotationSystem(), TextureRenderScale: stdsystems.NewTextureRenderScaleSystem(), TextureRenderTint: stdsystems.NewTextureRenderTintSystem(), - AssetLib: stdsystems.NewAssetLibSystem([]ecs.AnyAssetLibrary{assets.Textures}), + AssetLib: stdsystems.NewAssetLibSystem([]gomp.AnyAssetLibrary{assets.Textures}), Render: stdsystems.NewRenderSystem(), } - InjectECSToSystems(&newSystemList, world, components) + InjectECSToSystems(&newSystemList, entityManager, componentList) return newSystemList } @@ -75,7 +76,7 @@ type SystemList struct { type AnySystemList interface{} type AnyComponentList interface{} -func InjectECSToSystems(systemList AnySystemList, world *ecs.World, componentList AnyComponentList) { +func InjectECSToSystems(systemList AnySystemList, world *ecs.EntityManager, componentList AnyComponentList) { reflectedSystemList := reflect.ValueOf(systemList).Elem() systemsLen := reflectedSystemList.NumField() diff --git a/examples/new-api/scenes/main-scene.go b/examples/new-api/scenes/main-scene.go index 9ece27a8..290105a1 100644 --- a/examples/new-api/scenes/main-scene.go +++ b/examples/new-api/scenes/main-scene.go @@ -22,24 +22,20 @@ import ( ) func NewMainScene() *MainScene { - world := ecs.CreateWorld("Main") - comps := instances.NewComponentList(&world) - sys := instances.NewSystemList(&world, &comps) - - scene := MainScene{ - World: world, - Components: comps, - Systems: sys, - } + scene := new(MainScene) + + scene.EntityManager = ecs.NewEntityManager() + scene.ComponentList = instances.NewComponentList(&scene.EntityManager) + scene.SystemList = instances.NewSystemList(&scene.EntityManager, &scene.ComponentList) - return &scene + return scene } type MainScene struct { - Game *gomp.Game - World ecs.World - Components instances.ComponentList - Systems instances.SystemList + Game *gomp.Game + EntityManager ecs.EntityManager + ComponentList instances.ComponentList + SystemList instances.SystemList } func (s *MainScene) Id() gomp.SceneId { @@ -48,83 +44,80 @@ func (s *MainScene) Id() gomp.SceneId { func (s *MainScene) Init() { // Network receive - s.Systems.Network.Init() - s.Systems.NetworkReceive.Init() + s.SystemList.Network.Init() + s.SystemList.NetworkReceive.Init() // Scenes - s.Systems.Player.Init() + s.SystemList.Player.Init() - s.Systems.Velocity.Init() + s.SystemList.Velocity.Init() // Network patches - s.Systems.NetworkSend.Init() + s.SystemList.NetworkSend.Init() // Animation - s.Systems.AnimationSpriteMatrix.Init() - s.Systems.AnimationPlayer.Init() + s.SystemList.AnimationSpriteMatrix.Init() + s.SystemList.AnimationPlayer.Init() // Prerender init - s.Systems.TextureRenderSprite.Init() - s.Systems.TextureRenderSpriteSheet.Init() - s.Systems.TextureRenderMatrix.Init() + s.SystemList.TextureRenderSprite.Init() + s.SystemList.TextureRenderSpriteSheet.Init() + s.SystemList.TextureRenderMatrix.Init() // Prerender fill - s.Systems.TextureRenderAnimation.Init() - s.Systems.TextureRenderFlip.Init() - s.Systems.TextureRenderPosition.Init() - s.Systems.TextureRenderRotation.Init() - s.Systems.TextureRenderScale.Init() - s.Systems.TextureRenderTint.Init() + s.SystemList.TextureRenderAnimation.Init() + s.SystemList.TextureRenderFlip.Init() + s.SystemList.TextureRenderPosition.Init() + s.SystemList.TextureRenderRotation.Init() + s.SystemList.TextureRenderScale.Init() + s.SystemList.TextureRenderTint.Init() // Render - s.Systems.Render.Init() - s.Systems.Debug.Init() - s.Systems.AssetLib.Init() + s.SystemList.Render.Init() + s.SystemList.Debug.Init() + s.SystemList.AssetLib.Init() } func (s *MainScene) Update(dt time.Duration) gomp.SceneId { // Network receive - s.Systems.Network.Run(dt) - s.Systems.NetworkReceive.Run(dt) + s.SystemList.Network.Run(dt) + s.SystemList.NetworkReceive.Run(dt) - s.Systems.Player.Run(dt) + s.SystemList.Player.Run(dt) - // Network patches - s.Systems.NetworkSend.Run(dt) - - s.Systems.Debug.Run(dt) + s.SystemList.Debug.Run(dt) return MainSceneId } func (s *MainScene) FixedUpdate(dt time.Duration) { // Network send - s.Systems.NetworkSend.Run(dt) + s.SystemList.NetworkSend.Run(dt) } func (s *MainScene) Render(dt time.Duration) { - s.Systems.Velocity.Run(dt) + s.SystemList.Velocity.Run(dt) // Animation - s.Systems.AnimationSpriteMatrix.Run(dt) - s.Systems.AnimationPlayer.Run(dt) + s.SystemList.AnimationSpriteMatrix.Run(dt) + s.SystemList.AnimationPlayer.Run(dt) // Prerender init - s.Systems.TextureRenderSprite.Run(dt) - s.Systems.TextureRenderSpriteSheet.Run(dt) - s.Systems.TextureRenderMatrix.Run(dt) + s.SystemList.TextureRenderSprite.Run(dt) + s.SystemList.TextureRenderSpriteSheet.Run(dt) + s.SystemList.TextureRenderMatrix.Run(dt) // Prerender fill - s.Systems.TextureRenderAnimation.Run(dt) - s.Systems.TextureRenderFlip.Run(dt) - s.Systems.TextureRenderPosition.Run(dt) - s.Systems.TextureRenderRotation.Run(dt) - s.Systems.TextureRenderScale.Run(dt) - s.Systems.TextureRenderTint.Run(dt) + s.SystemList.TextureRenderAnimation.Run(dt) + s.SystemList.TextureRenderFlip.Run(dt) + s.SystemList.TextureRenderPosition.Run(dt) + s.SystemList.TextureRenderRotation.Run(dt) + s.SystemList.TextureRenderScale.Run(dt) + s.SystemList.TextureRenderTint.Run(dt) // Render - s.Systems.AssetLib.Run(dt) - shouldContinue := s.Systems.Render.Run(dt) + s.SystemList.AssetLib.Run(dt) + shouldContinue := s.SystemList.Render.Run(dt) if !shouldContinue { s.Game.SetShouldDestroy(true) return @@ -133,36 +126,35 @@ func (s *MainScene) Render(dt time.Duration) { func (s *MainScene) Destroy() { // Network intents - s.Systems.Network.Destroy() - s.Systems.NetworkReceive.Destroy() + s.SystemList.Network.Destroy() + s.SystemList.NetworkReceive.Destroy() - s.Systems.Player.Destroy() + s.SystemList.Player.Destroy() // Network patches - s.Systems.NetworkSend.Destroy() + s.SystemList.NetworkSend.Destroy() // Animation - s.Systems.AnimationSpriteMatrix.Destroy() - s.Systems.AnimationPlayer.Destroy() + s.SystemList.AnimationSpriteMatrix.Destroy() + s.SystemList.AnimationPlayer.Destroy() // Prerender init - s.Systems.TextureRenderSprite.Destroy() - s.Systems.TextureRenderSpriteSheet.Destroy() - s.Systems.TextureRenderMatrix.Destroy() + s.SystemList.TextureRenderSprite.Destroy() + s.SystemList.TextureRenderSpriteSheet.Destroy() + s.SystemList.TextureRenderMatrix.Destroy() // Prerender fill - s.Systems.TextureRenderAnimation.Destroy() - s.Systems.TextureRenderFlip.Destroy() - s.Systems.TextureRenderPosition.Destroy() - s.Systems.TextureRenderRotation.Destroy() - s.Systems.TextureRenderScale.Destroy() - s.Systems.TextureRenderTint.Destroy() + s.SystemList.TextureRenderAnimation.Destroy() + s.SystemList.TextureRenderFlip.Destroy() + s.SystemList.TextureRenderPosition.Destroy() + s.SystemList.TextureRenderRotation.Destroy() + s.SystemList.TextureRenderScale.Destroy() + s.SystemList.TextureRenderTint.Destroy() // Render - s.Systems.Debug.Destroy() - s.Systems.AssetLib.Destroy() - s.Systems.Render.Destroy() - + s.SystemList.Debug.Destroy() + s.SystemList.AssetLib.Destroy() + s.SystemList.Render.Destroy() } func (s *MainScene) OnEnter() { diff --git a/examples/new-api/systems/hp.go b/examples/new-api/systems/hp.go index 2fe51b95..c9025b6d 100644 --- a/examples/new-api/systems/hp.go +++ b/examples/new-api/systems/hp.go @@ -13,19 +13,19 @@ import ( type hpController struct{} -func (s *hpController) Init(world *ecs2.World) {} -func (s *hpController) Update(world *ecs2.World) {} -func (s *hpController) FixedUpdate(world *ecs2.World) { +func (s *hpController) Init(world *ecs2.EntityManager) {} +func (s *hpController) Update(world *ecs2.EntityManager) {} +func (s *hpController) FixedUpdate(world *ecs2.EntityManager) { healths := components.HealthService.GetManager(world) healths.All(func(entity ecs2.Entity, h *components.Health) bool { h.Hp-- if h.Hp <= 0 { - world.DestroyEntity(entity) + world.Delete(entity) } return true }) } -func (s *hpController) Destroy(world *ecs2.World) {} +func (s *hpController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/new-api/systems/player.go b/examples/new-api/systems/player.go index eec34562..0756e88b 100644 --- a/examples/new-api/systems/player.go +++ b/examples/new-api/systems/player.go @@ -19,7 +19,7 @@ func NewPlayerSystem() PlayerSystem { } type PlayerSystem struct { - World *ecs.World + World *ecs.EntityManager Player entities.Player SpriteMatrixes *stdcomponents.SpriteMatrixComponentManager Positions *stdcomponents.PositionComponentManager diff --git a/examples/raylib-ecs/assets/assets.go b/examples/raylib-ecs/assets/assets.go index c7383c04..76e4b25b 100644 --- a/examples/raylib-ecs/assets/assets.go +++ b/examples/raylib-ecs/assets/assets.go @@ -7,10 +7,10 @@ package assets import ( rl "github.com/gen2brain/raylib-go/raylib" - "gomp/pkg/ecs" + "gomp" ) -var Textures = ecs.CreateAssetLibrary( +var Textures = gomp.CreateAssetLibrary( func(path string) rl.Texture2D { return rl.LoadTexture(path) }, diff --git a/examples/raylib-ecs/entities/player.go b/examples/raylib-ecs/entities/player.go index 6e92ee91..aed5cfb2 100644 --- a/examples/raylib-ecs/entities/player.go +++ b/examples/raylib-ecs/entities/player.go @@ -64,7 +64,7 @@ var playerSpriteMatrix = components.SpriteMatrix{ }, } -func CreatePlayer(world *ecs2.World) (player Player) { +func CreatePlayer(world *ecs2.EntityManager) (player Player) { // Getting managers spriteMatrixes := components.SpriteMatrixService.GetManager(world) positions := components.PositionService.GetManager(world) @@ -77,7 +77,7 @@ func CreatePlayer(world *ecs2.World) (player Player) { // Creating new player - entity := world.CreateEntity("player") + entity := world.Create("player") player.Entity = entity // Adding position component diff --git a/examples/raylib-ecs/main.go b/examples/raylib-ecs/main.go index d89c0359..363116a6 100644 --- a/examples/raylib-ecs/main.go +++ b/examples/raylib-ecs/main.go @@ -21,7 +21,7 @@ import ( ) func main() { - world := ecs.CreateWorld("main-raylib") + world := ecs.NewEntityManager("main-raylib") defer world.Destroy() // world.ApplyPatch(patch) diff --git a/examples/raylib-ecs/systems/animation-player.go b/examples/raylib-ecs/systems/animation-player.go index 021906b5..b88c0164 100644 --- a/examples/raylib-ecs/systems/animation-player.go +++ b/examples/raylib-ecs/systems/animation-player.go @@ -17,8 +17,8 @@ import ( type animationPlayerController struct{} -func (s *animationPlayerController) Init(world *ecs.World) {} -func (s *animationPlayerController) Update(world *ecs.World) { +func (s *animationPlayerController) Init(world *ecs.EntityManager) {} +func (s *animationPlayerController) Update(world *ecs.EntityManager) { animationPlayers := components.AnimationPlayerService.GetManager(world) animationPlayers.AllDataParallel(func(animation *components.AnimationPlayer) bool { @@ -58,5 +58,5 @@ func (s *animationPlayerController) Update(world *ecs.World) { return true }) } -func (s *animationPlayerController) FixedUpdate(world *ecs.World) {} -func (s *animationPlayerController) Destroy(world *ecs.World) {} +func (s *animationPlayerController) FixedUpdate(world *ecs.EntityManager) {} +func (s *animationPlayerController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/animation-spritematrix.go b/examples/raylib-ecs/systems/animation-spritematrix.go index 00019b10..b53669e6 100644 --- a/examples/raylib-ecs/systems/animation-spritematrix.go +++ b/examples/raylib-ecs/systems/animation-spritematrix.go @@ -14,8 +14,8 @@ import ( type animationSpriteMatrixController struct{} -func (s *animationSpriteMatrixController) Init(world *ecs2.World) {} -func (s *animationSpriteMatrixController) Update(world *ecs2.World) { +func (s *animationSpriteMatrixController) Init(world *ecs2.EntityManager) {} +func (s *animationSpriteMatrixController) Update(world *ecs2.EntityManager) { animationPlayers := components.AnimationPlayerService.GetManager(world) animationStates := components.AnimationStateService.GetManager(world) spriteMatrixes := components.SpriteMatrixService.GetManager(world) @@ -51,5 +51,5 @@ func (s *animationSpriteMatrixController) Update(world *ecs2.World) { return true }) } -func (s *animationSpriteMatrixController) FixedUpdate(world *ecs2.World) {} -func (s *animationSpriteMatrixController) Destroy(world *ecs2.World) {} +func (s *animationSpriteMatrixController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *animationSpriteMatrixController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/asset-library.go b/examples/raylib-ecs/systems/asset-library.go index c63d6c65..bcec13f2 100644 --- a/examples/raylib-ecs/systems/asset-library.go +++ b/examples/raylib-ecs/systems/asset-library.go @@ -7,21 +7,22 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package systems import ( + "gomp" ecs2 "gomp/pkg/ecs" ) type assetLibController struct { - assets []ecs2.AnyAssetLibrary + assets []gomp.AnyAssetLibrary } -func (s *assetLibController) Init(world *ecs2.World) {} -func (s *assetLibController) Update(world *ecs2.World) { +func (s *assetLibController) Init(world *ecs2.EntityManager) {} +func (s *assetLibController) Update(world *ecs2.EntityManager) { for _, asset := range s.assets { asset.LoadAll() } } -func (s *assetLibController) FixedUpdate(world *ecs2.World) {} -func (s *assetLibController) Destroy(world *ecs2.World) { +func (s *assetLibController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *assetLibController) Destroy(world *ecs2.EntityManager) { for _, asset := range s.assets { asset.UnloadAll() } diff --git a/examples/raylib-ecs/systems/color.go b/examples/raylib-ecs/systems/color.go index 64b50561..e92eff64 100644 --- a/examples/raylib-ecs/systems/color.go +++ b/examples/raylib-ecs/systems/color.go @@ -16,11 +16,11 @@ type colorController struct { baseColor color.RGBA } -func (s *colorController) Init(world *ecs2.World) { +func (s *colorController) Init(world *ecs2.EntityManager) { s.baseColor = color.RGBA{25, 220, 200, 255} } -func (s *colorController) Update(world *ecs2.World) {} -func (s *colorController) FixedUpdate(world *ecs2.World) { +func (s *colorController) Update(world *ecs2.EntityManager) {} +func (s *colorController) FixedUpdate(world *ecs2.EntityManager) { sprites := components.SpriteService.GetManager(world) hps := components.HealthService.GetManager(world) @@ -41,4 +41,4 @@ func (s *colorController) FixedUpdate(world *ecs2.World) { return true }) } -func (s *colorController) Destroy(world *ecs2.World) {} +func (s *colorController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/debug.go b/examples/raylib-ecs/systems/debug.go index f05b417f..fc4bbf73 100644 --- a/examples/raylib-ecs/systems/debug.go +++ b/examples/raylib-ecs/systems/debug.go @@ -20,8 +20,8 @@ type debugController struct { pprofEnabled bool } -func (s *debugController) Init(world *ecs.World) {} -func (s *debugController) Update(world *ecs.World) { +func (s *debugController) Init(world *ecs.EntityManager) {} +func (s *debugController) Update(world *ecs.EntityManager) { if rl.IsKeyPressed(rl.KeyF9) { if s.pprofEnabled { pprof.StopCPUProfile() @@ -38,5 +38,5 @@ func (s *debugController) Update(world *ecs.World) { s.pprofEnabled = !s.pprofEnabled } } -func (s *debugController) FixedUpdate(world *ecs.World) {} -func (s *debugController) Destroy(world *ecs.World) {} +func (s *debugController) FixedUpdate(world *ecs.EntityManager) {} +func (s *debugController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/example.go b/examples/raylib-ecs/systems/example.go index 90c3b50f..ed3da188 100644 --- a/examples/raylib-ecs/systems/example.go +++ b/examples/raylib-ecs/systems/example.go @@ -20,7 +20,7 @@ import ( type exampleController struct{} -func (s *exampleController) Init(world *ecs.World) {} -func (s *exampleController) Update(world *ecs.World) {} -func (s *exampleController) FixedUpdate(world *ecs.World) {} -func (s *exampleController) Destroy(world *ecs.World) {} +func (s *exampleController) Init(world *ecs.EntityManager) {} +func (s *exampleController) Update(world *ecs.EntityManager) {} +func (s *exampleController) FixedUpdate(world *ecs.EntityManager) {} +func (s *exampleController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/hp.go b/examples/raylib-ecs/systems/hp.go index 2fe51b95..c9025b6d 100644 --- a/examples/raylib-ecs/systems/hp.go +++ b/examples/raylib-ecs/systems/hp.go @@ -13,19 +13,19 @@ import ( type hpController struct{} -func (s *hpController) Init(world *ecs2.World) {} -func (s *hpController) Update(world *ecs2.World) {} -func (s *hpController) FixedUpdate(world *ecs2.World) { +func (s *hpController) Init(world *ecs2.EntityManager) {} +func (s *hpController) Update(world *ecs2.EntityManager) {} +func (s *hpController) FixedUpdate(world *ecs2.EntityManager) { healths := components.HealthService.GetManager(world) healths.All(func(entity ecs2.Entity, h *components.Health) bool { h.Hp-- if h.Hp <= 0 { - world.DestroyEntity(entity) + world.Delete(entity) } return true }) } -func (s *hpController) Destroy(world *ecs2.World) {} +func (s *hpController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/init.go b/examples/raylib-ecs/systems/init.go index d81ded04..41a0f11c 100644 --- a/examples/raylib-ecs/systems/init.go +++ b/examples/raylib-ecs/systems/init.go @@ -13,8 +13,8 @@ import ( type initController struct { } -func (s *initController) Init(world *ecs.World) {} +func (s *initController) Init(world *ecs.EntityManager) {} -func (s *initController) Update(world *ecs.World) {} -func (s *initController) FixedUpdate(world *ecs.World) {} -func (s *initController) Destroy(world *ecs.World) {} +func (s *initController) Update(world *ecs.EntityManager) {} +func (s *initController) FixedUpdate(world *ecs.EntityManager) {} +func (s *initController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/network-receive.go b/examples/raylib-ecs/systems/network-receive.go index 9dc3a609..7f611453 100644 --- a/examples/raylib-ecs/systems/network-receive.go +++ b/examples/raylib-ecs/systems/network-receive.go @@ -20,7 +20,7 @@ import ( type networkReceiveController struct{} -func (s *networkReceiveController) Init(world *ecs.World) {} -func (s *networkReceiveController) Update(world *ecs.World) {} -func (s *networkReceiveController) FixedUpdate(world *ecs.World) {} -func (s *networkReceiveController) Destroy(world *ecs.World) {} +func (s *networkReceiveController) Init(world *ecs.EntityManager) {} +func (s *networkReceiveController) Update(world *ecs.EntityManager) {} +func (s *networkReceiveController) FixedUpdate(world *ecs.EntityManager) {} +func (s *networkReceiveController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/network-send.go b/examples/raylib-ecs/systems/network-send.go index 4ae622ca..2832b3f1 100644 --- a/examples/raylib-ecs/systems/network-send.go +++ b/examples/raylib-ecs/systems/network-send.go @@ -23,7 +23,7 @@ import ( type networkSendController struct{} -func (s *networkSendController) Init(world *ecs.World) { +func (s *networkSendController) Init(world *ecs.EntityManager) { positions := components.PositionService.GetManager(world) rotations := components.RotationService.GetManager(world) mirroreds := components.MirroredService.GetManager(world) @@ -60,8 +60,8 @@ func (s *networkSendController) Init(world *ecs.World) { return data }) } -func (s *networkSendController) Update(world *ecs.World) {} -func (s *networkSendController) FixedUpdate(world *ecs.World) { +func (s *networkSendController) Update(world *ecs.EntityManager) {} +func (s *networkSendController) FixedUpdate(world *ecs.EntityManager) { //patch := world.PatchGet() //world.PatchReset() //log.Printf("%v", patch) @@ -69,4 +69,4 @@ func (s *networkSendController) FixedUpdate(world *ecs.World) { network.Quic.Send([]byte("patch"), 0) } } -func (s *networkSendController) Destroy(world *ecs.World) {} +func (s *networkSendController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/network.go b/examples/raylib-ecs/systems/network.go index d50bb72e..b3fe5c93 100644 --- a/examples/raylib-ecs/systems/network.go +++ b/examples/raylib-ecs/systems/network.go @@ -31,9 +31,9 @@ const ( type networkController struct { } -func (s *networkController) Init(world *ecs.World) { +func (s *networkController) Init(world *ecs.EntityManager) { } -func (s *networkController) Update(world *ecs.World) { +func (s *networkController) Update(world *ecs.EntityManager) { if rl.IsKeyPressed(rl.KeyP) { network.Quic.Host("127.0.0.1:27015") } @@ -42,5 +42,5 @@ func (s *networkController) Update(world *ecs.World) { network.Quic.Connect("127.0.0.1:27015") } } -func (s *networkController) FixedUpdate(world *ecs.World) {} -func (s *networkController) Destroy(world *ecs.World) {} +func (s *networkController) FixedUpdate(world *ecs.EntityManager) {} +func (s *networkController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/player.go b/examples/raylib-ecs/systems/player.go index cd5f6658..8cc6b6cd 100644 --- a/examples/raylib-ecs/systems/player.go +++ b/examples/raylib-ecs/systems/player.go @@ -17,13 +17,13 @@ type playerController struct { player entities.Player } -func (s *playerController) Init(world *ecs.World) { +func (s *playerController) Init(world *ecs.EntityManager) { s.player = entities.CreatePlayer(world) s.player.Position.X = 100 s.player.Position.Y = 100 } -func (s *playerController) Update(world *ecs.World) { +func (s *playerController) Update(world *ecs.EntityManager) { animationStates := components.AnimationStateService.GetManager(world) animationState := animationStates.Get(s.player.Entity) @@ -52,5 +52,5 @@ func (s *playerController) Update(world *ecs.World) { } } } -func (s *playerController) FixedUpdate(world *ecs.World) {} -func (s *playerController) Destroy(world *ecs.World) {} +func (s *playerController) FixedUpdate(world *ecs.EntityManager) {} +func (s *playerController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/render.go b/examples/raylib-ecs/systems/render.go index d44d2df3..6bbeb492 100644 --- a/examples/raylib-ecs/systems/render.go +++ b/examples/raylib-ecs/systems/render.go @@ -17,12 +17,12 @@ type renderController struct { windowWidth, windowHeight int32 } -func (s *renderController) Init(world *ecs.World) { +func (s *renderController) Init(world *ecs.EntityManager) { rl.InitWindow(s.windowWidth, s.windowHeight, "raylib [core] ebiten-ecs - basic window") currentMonitorRefreshRate := int32(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) rl.SetTargetFPS(currentMonitorRefreshRate) } -func (s *renderController) Update(world *ecs.World) { +func (s *renderController) Update(world *ecs.EntityManager) { spriteRenders := components.TextureRenderService.GetManager(world) if rl.WindowShouldClose() { @@ -49,7 +49,7 @@ func (s *renderController) Update(world *ecs.World) { rl.EndDrawing() } -func (s *renderController) FixedUpdate(world *ecs.World) {} -func (s *renderController) Destroy(world *ecs.World) { +func (s *renderController) FixedUpdate(world *ecs.EntityManager) {} +func (s *renderController) Destroy(world *ecs.EntityManager) { rl.CloseWindow() } diff --git a/examples/raylib-ecs/systems/spawn.go b/examples/raylib-ecs/systems/spawn.go index 304dc6c2..6ef47b4c 100644 --- a/examples/raylib-ecs/systems/spawn.go +++ b/examples/raylib-ecs/systems/spawn.go @@ -25,8 +25,8 @@ const ( maxMaxHp = 2000 ) -func (s *spawnController) Init(world *ecs.World) {} -func (s *spawnController) Update(world *ecs.World) { +func (s *spawnController) Init(world *ecs.EntityManager) {} +func (s *spawnController) Update(world *ecs.EntityManager) { sprites := components.SpriteService.GetManager(world) healths := components.HealthService.GetManager(world) positions := components.PositionService.GetManager(world) @@ -39,7 +39,7 @@ func (s *spawnController) Update(world *ecs.World) { break } - newCreature := world.CreateEntity("Creature") + newCreature := world.Create("Creature") // Adding position component t := components.Position{ @@ -83,7 +83,7 @@ func (s *spawnController) Update(world *ecs.World) { } } } -func (s *spawnController) FixedUpdate(world *ecs.World) { +func (s *spawnController) FixedUpdate(world *ecs.EntityManager) { } -func (s *spawnController) Destroy(world *ecs.World) {} +func (s *spawnController) Destroy(world *ecs.EntityManager) {} diff --git a/examples/raylib-ecs/systems/systems-engine.go b/examples/raylib-ecs/systems/systems-engine.go index d7f7c6c2..c28b9a86 100644 --- a/examples/raylib-ecs/systems/systems-engine.go +++ b/examples/raylib-ecs/systems/systems-engine.go @@ -15,6 +15,7 @@ Thank you for your support! package systems import ( + "gomp" "gomp/examples/raylib-ecs/assets" "gomp/pkg/ecs" ) @@ -47,7 +48,7 @@ var TRTintService = ecs.CreateSystemService(&trTintController{}, &TRSpriteServic // Engine Render Systems var AssetLibService = ecs.CreateSystemService(&assetLibController{ - assets: []ecs.AnyAssetLibrary{assets.Textures}, + assets: []gomp.AnyAssetLibrary{assets.Textures}, }) var RenderService = ecs.CreateSystemService(&renderController{ windowWidth: 1024, diff --git a/examples/raylib-ecs/systems/texture-render-animation.go b/examples/raylib-ecs/systems/texture-render-animation.go index e3f25e3c..2bcea706 100644 --- a/examples/raylib-ecs/systems/texture-render-animation.go +++ b/examples/raylib-ecs/systems/texture-render-animation.go @@ -14,9 +14,9 @@ import ( // TextureRenderPosition is a system that sets Position of textureRender type trAnimationController struct{} -func (s *trAnimationController) Init(world *ecs2.World) {} -func (s *trAnimationController) FixedUpdate(world *ecs2.World) {} -func (s *trAnimationController) Update(world *ecs2.World) { +func (s *trAnimationController) Init(world *ecs2.EntityManager) {} +func (s *trAnimationController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trAnimationController) Update(world *ecs2.EntityManager) { // Get component managers animations := components.AnimationPlayerService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -42,4 +42,4 @@ func (s *trAnimationController) Update(world *ecs2.World) { return true }) } -func (s *trAnimationController) Destroy(world *ecs2.World) {} +func (s *trAnimationController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-mirrored.go b/examples/raylib-ecs/systems/texture-render-mirrored.go index fe2493a1..e8526e83 100644 --- a/examples/raylib-ecs/systems/texture-render-mirrored.go +++ b/examples/raylib-ecs/systems/texture-render-mirrored.go @@ -14,9 +14,9 @@ import ( // TextureRenderScale is a system that sets Scale of textureRender type trMirroredController struct{} -func (s *trMirroredController) Init(world *ecs2.World) {} -func (s *trMirroredController) FixedUpdate(world *ecs2.World) {} -func (s *trMirroredController) Update(world *ecs2.World) { +func (s *trMirroredController) Init(world *ecs2.EntityManager) {} +func (s *trMirroredController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trMirroredController) Update(world *ecs2.EntityManager) { // Get component managers mirroreds := components.MirroredService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -42,4 +42,4 @@ func (s *trMirroredController) Update(world *ecs2.World) { return true }) } -func (s *trMirroredController) Destroy(world *ecs2.World) {} +func (s *trMirroredController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-position.go b/examples/raylib-ecs/systems/texture-render-position.go index 18c3cfed..a29ea4be 100644 --- a/examples/raylib-ecs/systems/texture-render-position.go +++ b/examples/raylib-ecs/systems/texture-render-position.go @@ -14,9 +14,9 @@ import ( // TextureRenderPosition is a system that sets Position of textureRender type trPositionController struct{} -func (s *trPositionController) Init(world *ecs2.World) {} -func (s *trPositionController) FixedUpdate(world *ecs2.World) {} -func (s *trPositionController) Update(world *ecs2.World) { +func (s *trPositionController) Init(world *ecs2.EntityManager) {} +func (s *trPositionController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trPositionController) Update(world *ecs2.EntityManager) { // Get component managers positions := components.PositionService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -38,4 +38,4 @@ func (s *trPositionController) Update(world *ecs2.World) { return true }) } -func (s *trPositionController) Destroy(world *ecs2.World) {} +func (s *trPositionController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-rotation.go b/examples/raylib-ecs/systems/texture-render-rotation.go index 24648b04..6dd91037 100644 --- a/examples/raylib-ecs/systems/texture-render-rotation.go +++ b/examples/raylib-ecs/systems/texture-render-rotation.go @@ -14,9 +14,9 @@ import ( // TextureRenderRotation is a system that sets Rotation of textureRender type trRotationController struct{} -func (s *trRotationController) Init(world *ecs2.World) {} -func (s *trRotationController) FixedUpdate(world *ecs2.World) {} -func (s *trRotationController) Update(world *ecs2.World) { +func (s *trRotationController) Init(world *ecs2.EntityManager) {} +func (s *trRotationController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trRotationController) Update(world *ecs2.EntityManager) { // Get component managers rotations := components.RotationService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -37,4 +37,4 @@ func (s *trRotationController) Update(world *ecs2.World) { return true }) } -func (s *trRotationController) Destroy(world *ecs2.World) {} +func (s *trRotationController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-scale.go b/examples/raylib-ecs/systems/texture-render-scale.go index 08d6be38..b415b0bb 100644 --- a/examples/raylib-ecs/systems/texture-render-scale.go +++ b/examples/raylib-ecs/systems/texture-render-scale.go @@ -14,9 +14,9 @@ import ( // TextureRenderScale is a system that sets Scale of textureRender type trScaleController struct{} -func (s *trScaleController) Init(world *ecs2.World) {} -func (s *trScaleController) FixedUpdate(world *ecs2.World) {} -func (s *trScaleController) Update(world *ecs2.World) { +func (s *trScaleController) Init(world *ecs2.EntityManager) {} +func (s *trScaleController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trScaleController) Update(world *ecs2.EntityManager) { // Get component managers scales := components.ScaleService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -38,4 +38,4 @@ func (s *trScaleController) Update(world *ecs2.World) { return true }) } -func (s *trScaleController) Destroy(world *ecs2.World) {} +func (s *trScaleController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-sprite.go b/examples/raylib-ecs/systems/texture-render-sprite.go index 4d0ec0c5..79b0f506 100644 --- a/examples/raylib-ecs/systems/texture-render-sprite.go +++ b/examples/raylib-ecs/systems/texture-render-sprite.go @@ -23,9 +23,9 @@ import ( // TextureRenderSprite is a system that prepares Sprite to be rendered type trSpriteController struct{} -func (s *trSpriteController) Init(world *ecs2.World) {} -func (s *trSpriteController) FixedUpdate(world *ecs2.World) {} -func (s *trSpriteController) Update(world *ecs2.World) { +func (s *trSpriteController) Init(world *ecs2.EntityManager) {} +func (s *trSpriteController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trSpriteController) Update(world *ecs2.EntityManager) { // Get component managers sprites := components.SpriteService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -83,4 +83,4 @@ func (s *trSpriteController) Update(world *ecs2.World) { return true }) } -func (s *trSpriteController) Destroy(world *ecs2.World) {} +func (s *trSpriteController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-spritematrix.go b/examples/raylib-ecs/systems/texture-render-spritematrix.go index 1a31b34b..ae0e5568 100644 --- a/examples/raylib-ecs/systems/texture-render-spritematrix.go +++ b/examples/raylib-ecs/systems/texture-render-spritematrix.go @@ -15,9 +15,9 @@ import ( // TextureRenderSprite is a system that prepares SpriteSheet to be rendered type trSpriteMatrixController struct{} -func (s *trSpriteMatrixController) Init(world *ecs2.World) {} -func (s *trSpriteMatrixController) FixedUpdate(world *ecs2.World) {} -func (s *trSpriteMatrixController) Update(world *ecs2.World) { +func (s *trSpriteMatrixController) Init(world *ecs2.EntityManager) {} +func (s *trSpriteMatrixController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trSpriteMatrixController) Update(world *ecs2.EntityManager) { // Get component managers spriteMatrixes := components.SpriteMatrixService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -63,4 +63,4 @@ func (s *trSpriteMatrixController) Update(world *ecs2.World) { return true }) } -func (s *trSpriteMatrixController) Destroy(world *ecs2.World) {} +func (s *trSpriteMatrixController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-spritesheet.go b/examples/raylib-ecs/systems/texture-render-spritesheet.go index 897b3057..0ef84aaf 100644 --- a/examples/raylib-ecs/systems/texture-render-spritesheet.go +++ b/examples/raylib-ecs/systems/texture-render-spritesheet.go @@ -15,9 +15,9 @@ import ( // TextureRenderSprite is a system that prepares SpriteSheet to be rendered type trSpriteSheetController struct{} -func (s *trSpriteSheetController) Init(world *ecs2.World) {} -func (s *trSpriteSheetController) FixedUpdate(world *ecs2.World) {} -func (s *trSpriteSheetController) Update(world *ecs2.World) { +func (s *trSpriteSheetController) Init(world *ecs2.EntityManager) {} +func (s *trSpriteSheetController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trSpriteSheetController) Update(world *ecs2.EntityManager) { // Get component managers spriteSheets := components.SpriteSheetService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -53,4 +53,4 @@ func (s *trSpriteSheetController) Update(world *ecs2.World) { return true }) } -func (s *trSpriteSheetController) Destroy(world *ecs2.World) {} +func (s *trSpriteSheetController) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/raylib-ecs/systems/texture-render-tint.go b/examples/raylib-ecs/systems/texture-render-tint.go index 48881870..d8928ee3 100644 --- a/examples/raylib-ecs/systems/texture-render-tint.go +++ b/examples/raylib-ecs/systems/texture-render-tint.go @@ -14,9 +14,9 @@ import ( // TextureRenderScale is a system that sets Scale of textureRender type trTintController struct{} -func (s *trTintController) Init(world *ecs2.World) {} -func (s *trTintController) FixedUpdate(world *ecs2.World) {} -func (s *trTintController) Update(world *ecs2.World) { +func (s *trTintController) Init(world *ecs2.EntityManager) {} +func (s *trTintController) FixedUpdate(world *ecs2.EntityManager) {} +func (s *trTintController) Update(world *ecs2.EntityManager) { // Get component managers tints := components.TintService.GetManager(world) textureRenders := components.TextureRenderService.GetManager(world) @@ -41,4 +41,4 @@ func (s *trTintController) Update(world *ecs2.World) { return true }) } -func (s *trTintController) Destroy(world *ecs2.World) {} +func (s *trTintController) Destroy(world *ecs2.EntityManager) {} diff --git a/pkg/ecs/component-manager.go b/pkg/ecs/component-manager.go index e63b1d7d..2c78f70a 100644 --- a/pkg/ecs/component-manager.go +++ b/pkg/ecs/component-manager.go @@ -7,72 +7,127 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs import ( - "iter" "math/big" "sync" "github.com/negrel/assert" ) -type ComponentManagerX[T any] struct { - mx *sync.Mutex - components *PagedArray[T] - entities *PagedArray[Entity] - lookup *PagedMap[Entity, int32] - ID ComponentID - isInitialized bool +// ================ +// Contracts +// ================ + +type AnyComponentManagerPtr interface { + registerComponentMask(mask *ComponentManager[big.Int]) + getId() ComponentID + Remove(Entity) + Clean() + Has(Entity) bool + PatchAdd(Entity) + PatchGet() ComponentPatch + PatchApply(patch ComponentPatch) + PatchReset() + IsTrackingChanges() bool +} + +// ================ +// Service +// ================ + +func NewComponentManager[T any](entityManager *EntityManager, id ComponentID) ComponentManager[T] { + newManager := ComponentManager[T]{ + mx: new(sync.Mutex), + + components: NewPagedArray[T](), + entities: NewPagedArray[Entity](), + lookup: NewPagedMap[Entity, int32](), + + maskComponent: entityManager.entityComponentMask, + id: id, + isInitialized: true, + + TrackChanges: false, + createdEntities: NewPagedArray[Entity](), + patchedEntities: NewPagedArray[Entity](), + deletedEntities: NewPagedArray[Entity](), + } + + return newManager } -type ComponentSeviceX[T any] struct { - id ComponentID - managers map[*World]*ComponentManagerX[T] +type ComponentManager[T any] struct { + mx *sync.Mutex + + components *PagedArray[T] + entities *PagedArray[Entity] + lookup *PagedMap[Entity, int32] + + maskComponent *SparseSet[ComponentBitArray256, Entity] + id ComponentID + isInitialized bool + + // Patch + + TrackChanges bool // Enable TrackChanges to track changes and add them to patch + createdEntities *PagedArray[Entity] + patchedEntities *PagedArray[Entity] + deletedEntities *PagedArray[Entity] + + encoder func([]T) []byte + decoder func([]byte) []T } -func (m *ComponentSeviceX[T]) Instance(world *World) *ComponentManagerX[T] { - instance, ok := m.managers[world] - assert.True(ok) - return instance +// ComponentChanges with byte encoded Components +type ComponentChanges struct { + Len int32 + Components []byte + Entities []Entity } -func CreateComponentServiceX[T any](id ComponentID) *ComponentSeviceX[T] { - return &ComponentSeviceX[T]{ - id: id, - managers: make(map[*World]*ComponentManagerX[T]), - } +// ComponentPatch with byte encoded Created, Patched and Deleted components +type ComponentPatch struct { + ID ComponentID + Created ComponentChanges + Patched ComponentChanges + Deleted ComponentChanges } -func (c *ComponentManagerX[T]) registerComponentMask(mask *ComponentManagerX[big.Int]) { - // c.worldMask = mask +func (c *ComponentManager[T]) getId() ComponentID { + return c.id } -func (c *ComponentManagerX[T]) getId() ComponentID { - return c.ID +func (c *ComponentManager[T]) registerComponentMask(*ComponentManager[big.Int]) { } -func (c *ComponentManagerX[T]) Create(entity Entity, value T) (returnValue *T) { +//===================================== +//===================================== +//===================================== + +func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { c.mx.Lock() defer c.mx.Unlock() - // ComponentManager must be initialized with CreateComponentManager() - assert.True(c.isInitialized) - - // Only one of component per enity allowed! - assert.False(c.Has(entity)) - - // Entity Count must always be the same as the number of components! - assert.True(c.entities.Len() == c.components.Len()) - assert.True(c.components.Len() == c.lookup.Len()) + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.False(c.Has(entity), "Only one of component per entity allowed!") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") var index = c.components.Len() c.lookup.Set(entity, index) c.entities.Append(entity) - return c.components.Append(value) + component = c.components.Append(value) + + mask := c.maskComponent.GetPtr(entity) + mask.Set(c.id) + + c.createdEntities.Append(entity) + + return component } -func (c *ComponentManagerX[T]) Get(entity Entity) *T { - // ComponentManager must be initialized with CreateComponentManager() - assert.True(c.isInitialized) +func (c *ComponentManager[T]) Get(entity Entity) (component *T) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") index, ok := c.lookup.Get(entity) if !ok { @@ -82,21 +137,34 @@ func (c *ComponentManagerX[T]) Get(entity Entity) *T { return c.components.Get(index) } -func (c *ComponentManagerX[T]) Remove(entity Entity) { +func (c *ComponentManager[T]) Set(entity Entity, value T) *T { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + + index, ok := c.lookup.Get(entity) + if !ok { + return nil + } + + component := c.components.Set(index, value) + + c.patchedEntities.Append(entity) + + return component +} + +func (c *ComponentManager[T]) Remove(entity Entity) { c.mx.Lock() defer c.mx.Unlock() - // ComponentManager must be initialized with CreateComponentManager() - assert.True(c.isInitialized) - - // ENTITY HAS NO COMPONENT! - assert.True(c.Has(entity)) + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") index, _ := c.lookup.Get(entity) lastIndex := c.components.Len() - 1 if index < lastIndex { - // Swap the the dead element with the last one + // Swap the dead element with the last one c.components.Swap(index, lastIndex) newSwappedEntityId, _ := c.entities.Swap(index, lastIndex) assert.True(newSwappedEntityId != nil) @@ -110,94 +178,186 @@ func (c *ComponentManagerX[T]) Remove(entity Entity) { c.entities.SoftReduce() c.lookup.Delete(entity) + mask := c.maskComponent.GetPtr(entity) + mask.Unset(c.id) - // Entity Count must always be the same as the number of components! - assert.True(c.entities.Len() == c.components.Len()) - assert.True(c.components.Len() == c.lookup.Len()) + c.deletedEntities.Append(entity) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") } -func (c *ComponentManagerX[T]) Has(entity Entity) bool { +func (c *ComponentManager[T]) Has(entity Entity) bool { _, ok := c.lookup.Get(entity) return ok } -func (c *ComponentManagerX[T]) All(yield func(Entity, *T) bool) { - // ComponentManager must be initialized with CreateComponentManager() - assert.True(c.isInitialized) - - // Entity Count must always be the same as the number of components! - assert.True(c.entities.Len() == c.components.Len()) - assert.True(c.components.Len() == c.lookup.Len()) - - nextData, stopData := iter.Pull(c.components.AllData) - defer stopData() - - nextEntity, stopEntity := iter.Pull(c.entities.AllData) - defer stopEntity() - - for { - data, ok := nextData() - if !ok { - break - } - entityId, ok := nextEntity() - if !ok { - break - } - assert.True(entityId != nil) - entId := *entityId - shouldContinue := yield(entId, data) - if !shouldContinue { - break - } +// Patches + +func (c *ComponentManager[T]) PatchAdd(entity Entity) { + assert.True(c.TrackChanges) + + c.patchedEntities.Append(entity) +} + +func (c *ComponentManager[T]) PatchGet() ComponentPatch { + assert.True(c.TrackChanges) + + patch := ComponentPatch{ + ID: c.id, + Created: c.getChangesBinary(c.createdEntities), + Patched: c.getChangesBinary(c.patchedEntities), + Deleted: c.getChangesBinary(c.deletedEntities), + } + + return patch +} + +func (c *ComponentManager[T]) PatchApply(patch ComponentPatch) { + assert.True(c.TrackChanges) + assert.True(patch.ID == c.id) + assert.True(c.decoder != nil) + + var components []T + + created := patch.Created + components = c.decoder(created.Components) + for i := range created.Len { + c.Create(created.Entities[i], components[i]) } + + patched := patch.Patched + components = c.decoder(patched.Components) + for i := range patched.Len { + c.Set(patched.Entities[i], components[i]) + } + + deleted := patch.Deleted + components = c.decoder(deleted.Components) + for i := range deleted.Len { + c.Remove(deleted.Entities[i]) + } +} + +func (c *ComponentManager[T]) PatchReset() { + assert.True(c.TrackChanges) + + c.createdEntities.Reset() + c.patchedEntities.Reset() + c.deletedEntities.Reset() } -func (c *ComponentManagerX[T]) AllParallel(yield func(Entity, *T) bool) { - // ComponentManager must be initialized with CreateComponentManager() - assert.True(c.isInitialized) +func (c *ComponentManager[T]) getChangesBinary(source *PagedArray[Entity]) ComponentChanges { + changesLen := source.Len() - // Entity Count must always be the same as the number of components! - assert.True(c.entities.Len() == c.components.Len()) - assert.True(c.components.Len() == c.lookup.Len()) + components := make([]T, 0, changesLen) + entities := make([]Entity, 0, changesLen) - c.components.AllParallel(func(i int32, t *T) bool { - entId := c.entities.Get(i) - assert.True(entId != nil) - return yield(*entId, t) + source.AllData(func(e *Entity) bool { + assert.True(e != nil) + entId := *e + assert.True(c.Has(entId)) + components = append(components, *c.Get(entId)) + entities = append(entities, entId) + return true }) + + assert.True(c.encoder != nil) + + componentsBinary := c.encoder(components) + + return ComponentChanges{ + Len: changesLen, + Components: componentsBinary, + Entities: entities, + } } -func (c *ComponentManagerX[T]) AllData(yield func(*T) bool) { - // ComponentManager must be initialized with CreateComponentManager() - assert.True(c.isInitialized) +func (c *ComponentManager[T]) SetEncoder(function func(components []T) []byte) *ComponentManager[T] { + c.encoder = function + return c +} - // Entity Count must always be the same as the number of components! - assert.True(c.entities.Len() == c.components.Len()) - assert.True(c.components.Len() == c.lookup.Len()) +func (c *ComponentManager[T]) SetDecoder(function func(data []byte) []T) *ComponentManager[T] { + c.decoder = function + return c +} - c.components.AllData(yield) +func (c *ComponentManager[T]) IsTrackingChanges() bool { + return c.TrackChanges } -func (c *ComponentManagerX[T]) AllDataParallel(yield func(*T) bool) { - // ComponentManager must be initialized with CreateComponentManager() - assert.True(c.isInitialized) +// Iterators - // Entity Count must always be the same as the number of components! - assert.True(c.entities.Len() == c.components.Len()) - assert.True(c.components.Len() == c.lookup.Len()) +func (c *ComponentManager[T]) All(yield func(Entity, *T) bool) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + + c.components.All(func(i int32, d *T) bool { + assert.True(d != nil) + entity := c.entities.Get(i) + assert.True(entity != nil) + entId := *entity + shouldContinue := yield(entId, d) + return shouldContinue + }) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} + +func (c *ComponentManager[T]) AllParallel(yield func(Entity, *T) bool) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + + c.components.AllParallel(func(i int32, t *T) bool { + entity := c.entities.Get(i) + assert.True(entity != nil) + entId := *entity + shouldContinue := yield(entId, t) + return shouldContinue + }) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} + +func (c *ComponentManager[T]) AllData(yield func(*T) bool) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") + + c.components.AllData(yield) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") +} + +func (c *ComponentManager[T]) AllDataParallel(yield func(*T) bool) { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") c.components.AllDataParallel(yield) + + assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") + assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") } -func (c *ComponentManagerX[T]) Len() int32 { - // ComponentManager must be initialized with CreateComponentManager() - assert.True(c.isInitialized) +// Utils + +func (c *ComponentManager[T]) Len() int32 { + assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") return c.components.Len() } -func (c *ComponentManagerX[T]) Clean() { +func (c *ComponentManager[T]) Clean() { + c.maskComponent.Clean() // c.components.Clean() // c.entities.Clean() } diff --git a/pkg/ecs/component_test.go b/pkg/ecs/component-manager_test.go similarity index 85% rename from pkg/ecs/component_test.go rename to pkg/ecs/component-manager_test.go index 95d25f55..54e74ef0 100644 --- a/pkg/ecs/component_test.go +++ b/pkg/ecs/component-manager_test.go @@ -23,8 +23,8 @@ type pixel struct { var pixelComponentType = CreateComponentService[pixel](1) // Commonly used functions in both benchmarks. -func PrepareWorld(description string, system AnySystemServicePtr) *World { - world := CreateWorld(description) +func PrepareWorld(description string, system AnySystemServicePtr) *EntityManager { + world := NewEntityManager(description) world.RegisterComponentServices( &pixelComponentType, @@ -37,13 +37,13 @@ func PrepareWorld(description string, system AnySystemServicePtr) *World { return world } -func InitPixelComponent(pixelComponent *ComponentManager[pixel], world *World) { +func InitPixelComponent(pixelComponent *ComponentManager[pixel], world *EntityManager) { pixelComponent = pixelComponentType.GetManager(world) determRand := rand.New(rand.NewSource(42)) for i := range 1000 { for j := range 1000 { - newPixel := world.CreateEntity("Pixel") + newPixel := world.Create("Pixel") randomGreen := uint8(135 / (determRand.Intn(10) + 1)) randomBlue := uint8(135 / (determRand.Intn(10) + 1)) @@ -67,14 +67,14 @@ type pixelSystem struct { pixelComponent ComponentManager[pixel] } -func (s *pixelSystem) Init(world *World) { +func (s *pixelSystem) Init(world *EntityManager) { InitPixelComponent(&s.pixelComponent, world) } -func (s *pixelSystem) Destroy(world *World) {} +func (s *pixelSystem) Destroy(world *EntityManager) {} -func (s *pixelSystem) Update(world *World) {} -func (s *pixelSystem) FixedUpdate(world *World) { +func (s *pixelSystem) Update(world *EntityManager) {} +func (s *pixelSystem) FixedUpdate(world *EntityManager) { for pixel := range s.pixelComponent.AllData { // Note: was not extracted to separate function to simulate // real-world interaction between range loop and inner code. @@ -117,13 +117,13 @@ type pixelSystemDirectCall struct { pixelComponent ComponentManager[pixel] } -func (s *pixelSystemDirectCall) Init(world *World) { +func (s *pixelSystemDirectCall) Init(world *EntityManager) { InitPixelComponent(&s.pixelComponent, world) } -func (s *pixelSystemDirectCall) Destroy(world *World) {} -func (s *pixelSystemDirectCall) Update(world *World) {} -func (s *pixelSystemDirectCall) FixedUpdate(world *World) { +func (s *pixelSystemDirectCall) Destroy(world *EntityManager) {} +func (s *pixelSystemDirectCall) Update(world *EntityManager) {} +func (s *pixelSystemDirectCall) FixedUpdate(world *EntityManager) { s.pixelComponent.AllDataParallel(func(pixel *pixel) bool { color := &pixel.color diff --git a/pkg/ecs/component-service.go b/pkg/ecs/component-service.go new file mode 100644 index 00000000..634b998c --- /dev/null +++ b/pkg/ecs/component-service.go @@ -0,0 +1,64 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +none :) + +Thank you for your support! +*/ + +package ecs + +import ( + "fmt" + "github.com/negrel/assert" + "sync" +) + +type AnyComponentServicePtr interface { + register(*EntityManager) AnyComponentManagerPtr + getId() ComponentID +} + +type ComponentService[T any] struct { + id ComponentID + managers map[*EntityManager]*ComponentManager[T] +} + +func (c *ComponentService[T]) GetManager(world *EntityManager) *ComponentManager[T] { + manager, ok := c.managers[world] + assert.True(ok, fmt.Sprintf("Component <%T> is not registered in world", c)) + return manager +} + +func (c *ComponentService[T]) register(world *EntityManager) AnyComponentManagerPtr { + newManager := ComponentManager[T]{ + mx: new(sync.Mutex), + + components: NewPagedArray[T](), + entities: NewPagedArray[Entity](), + lookup: NewPagedMap[Entity, int32](), + + maskComponent: world.entityComponentMask, + id: c.id, + isInitialized: true, + + TrackChanges: false, + createdEntities: NewPagedArray[Entity](), + patchedEntities: NewPagedArray[Entity](), + deletedEntities: NewPagedArray[Entity](), + } + + c.managers[world] = &newManager + + return &newManager +} + +func (c *ComponentService[T]) getId() ComponentID { + return c.id +} diff --git a/pkg/ecs/component.go b/pkg/ecs/component.go deleted file mode 100644 index 5bdf5e53..00000000 --- a/pkg/ecs/component.go +++ /dev/null @@ -1,411 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package ecs - -import ( - "fmt" - "math/big" - "sync" - - "github.com/negrel/assert" -) - -// ================ -// Contracts -// ================ - -type AnyComponentServicePtr interface { - register(*World) AnyComponentManagerPtr - getId() ComponentID -} - -type AnyComponentManagerPtr interface { - registerComponentMask(mask *ComponentManager[big.Int]) - getId() ComponentID - Remove(Entity) - Clean() - Has(Entity) bool - PatchAdd(Entity) - PatchGet() ComponentPatch - PatchApply(patch ComponentPatch) - PatchReset() - IsTrackingChanges() bool -} - -// ================ -// Service -// ================ - -type ComponentService[T any] struct { - id ComponentID - managers map[*World]*ComponentManager[T] -} - -func (c *ComponentService[T]) GetManager(world *World) *ComponentManager[T] { - manager, ok := c.managers[world] - assert.True(ok, fmt.Sprintf("Component <%T> is not registered in <%s> world", c, world.Title)) - return manager -} - -func (c *ComponentService[T]) register(world *World) AnyComponentManagerPtr { - newManager := ComponentManager[T]{ - mx: new(sync.Mutex), - - components: NewPagedArray[T](), - entities: NewPagedArray[Entity](), - lookup: NewPagedMap[Entity, int32](), - - maskComponent: world.entityComponentMask, - id: c.id, - isInitialized: true, - - TrackChanges: false, - createdEntities: NewPagedArray[Entity](), - patchedEntities: NewPagedArray[Entity](), - deletedEntities: NewPagedArray[Entity](), - } - - c.managers[world] = &newManager - - return &newManager -} - -func (c *ComponentService[T]) getId() ComponentID { - return c.id -} - -// ================ -// Service -// ================ - -func NewComponentManager[T any](world *World, id ComponentID) ComponentManager[T] { - newManager := ComponentManager[T]{ - mx: new(sync.Mutex), - - components: NewPagedArray[T](), - entities: NewPagedArray[Entity](), - lookup: NewPagedMap[Entity, int32](), - - maskComponent: world.entityComponentMask, - id: id, - isInitialized: true, - - TrackChanges: false, - createdEntities: NewPagedArray[Entity](), - patchedEntities: NewPagedArray[Entity](), - deletedEntities: NewPagedArray[Entity](), - } - - return newManager -} - -type ComponentManager[T any] struct { - mx *sync.Mutex - - components *PagedArray[T] - entities *PagedArray[Entity] - lookup *PagedMap[Entity, int32] - - maskComponent *SparseSet[ComponentBitArray256, Entity] - id ComponentID - isInitialized bool - - // Patch - - TrackChanges bool // Enable TrackChanges to track changes and add them to patch - createdEntities *PagedArray[Entity] - patchedEntities *PagedArray[Entity] - deletedEntities *PagedArray[Entity] - - encoder func([]T) []byte - decoder func([]byte) []T -} - -// ComponentChanges with byte encoded Components -type ComponentChanges struct { - Len int32 - Components []byte - Entities []Entity -} - -// ComponentPatch with byte encoded Created, Patched and Deleted components -type ComponentPatch struct { - ID ComponentID - Created ComponentChanges - Patched ComponentChanges - Deleted ComponentChanges -} - -func (c *ComponentManager[T]) getId() ComponentID { - return c.id -} - -func (c *ComponentManager[T]) registerComponentMask(*ComponentManager[big.Int]) { -} - -//===================================== -//===================================== -//===================================== - -func (c *ComponentManager[T]) Create(entity Entity, value T) (component *T) { - c.mx.Lock() - defer c.mx.Unlock() - - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.False(c.Has(entity), "Only one of component per entity allowed!") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - var index = c.components.Len() - - c.lookup.Set(entity, index) - c.entities.Append(entity) - component = c.components.Append(value) - - mask := c.maskComponent.GetPtr(entity) - mask.Set(c.id) - - c.createdEntities.Append(entity) - - return component -} - -func (c *ComponentManager[T]) Get(entity Entity) (component *T) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - - index, ok := c.lookup.Get(entity) - if !ok { - return nil - } - - return c.components.Get(index) -} - -func (c *ComponentManager[T]) Set(entity Entity, value T) *T { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - - index, ok := c.lookup.Get(entity) - if !ok { - return nil - } - - component := c.components.Set(index, value) - - c.patchedEntities.Append(entity) - - return component -} - -func (c *ComponentManager[T]) Remove(entity Entity) { - c.mx.Lock() - defer c.mx.Unlock() - - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - index, _ := c.lookup.Get(entity) - - lastIndex := c.components.Len() - 1 - if index < lastIndex { - // Swap the dead element with the last one - c.components.Swap(index, lastIndex) - newSwappedEntityId, _ := c.entities.Swap(index, lastIndex) - assert.True(newSwappedEntityId != nil) - - // Update the lookup table - c.lookup.Set(*newSwappedEntityId, index) - } - - // Shrink the container - c.components.SoftReduce() - c.entities.SoftReduce() - - c.lookup.Delete(entity) - mask := c.maskComponent.GetPtr(entity) - mask.Unset(c.id) - - c.deletedEntities.Append(entity) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -func (c *ComponentManager[T]) Has(entity Entity) bool { - _, ok := c.lookup.Get(entity) - return ok -} - -// Patches - -func (c *ComponentManager[T]) PatchAdd(entity Entity) { - assert.True(c.TrackChanges) - - c.patchedEntities.Append(entity) -} - -func (c *ComponentManager[T]) PatchGet() ComponentPatch { - assert.True(c.TrackChanges) - - patch := ComponentPatch{ - ID: c.id, - Created: c.getChangesBinary(c.createdEntities), - Patched: c.getChangesBinary(c.patchedEntities), - Deleted: c.getChangesBinary(c.deletedEntities), - } - - return patch -} - -func (c *ComponentManager[T]) PatchApply(patch ComponentPatch) { - assert.True(c.TrackChanges) - assert.True(patch.ID == c.id) - assert.True(c.decoder != nil) - - var components []T - - created := patch.Created - components = c.decoder(created.Components) - for i := range created.Len { - c.Create(created.Entities[i], components[i]) - } - - patched := patch.Patched - components = c.decoder(patched.Components) - for i := range patched.Len { - c.Set(patched.Entities[i], components[i]) - } - - deleted := patch.Deleted - components = c.decoder(deleted.Components) - for i := range deleted.Len { - c.Remove(deleted.Entities[i]) - } -} - -func (c *ComponentManager[T]) PatchReset() { - assert.True(c.TrackChanges) - - c.createdEntities.Reset() - c.patchedEntities.Reset() - c.deletedEntities.Reset() -} - -func (c *ComponentManager[T]) getChangesBinary(source *PagedArray[Entity]) ComponentChanges { - changesLen := source.Len() - - components := make([]T, 0, changesLen) - entities := make([]Entity, 0, changesLen) - - source.AllData(func(e *Entity) bool { - assert.True(e != nil) - entId := *e - assert.True(c.Has(entId)) - components = append(components, *c.Get(entId)) - entities = append(entities, entId) - return true - }) - - assert.True(c.encoder != nil) - - componentsBinary := c.encoder(components) - - return ComponentChanges{ - Len: changesLen, - Components: componentsBinary, - Entities: entities, - } -} - -func (c *ComponentManager[T]) SetEncoder(function func(components []T) []byte) *ComponentManager[T] { - c.encoder = function - return c -} - -func (c *ComponentManager[T]) SetDecoder(function func(data []byte) []T) *ComponentManager[T] { - c.decoder = function - return c -} - -func (c *ComponentManager[T]) IsTrackingChanges() bool { - return c.TrackChanges -} - -// Iterators - -func (c *ComponentManager[T]) All(yield func(Entity, *T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.All(func(i int32, d *T) bool { - assert.True(d != nil) - entity := c.entities.Get(i) - assert.True(entity != nil) - entId := *entity - shouldContinue := yield(entId, d) - return shouldContinue - }) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -func (c *ComponentManager[T]) AllParallel(yield func(Entity, *T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.AllParallel(func(i int32, t *T) bool { - entity := c.entities.Get(i) - assert.True(entity != nil) - entId := *entity - shouldContinue := yield(entId, t) - return shouldContinue - }) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -func (c *ComponentManager[T]) AllData(yield func(*T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.AllData(yield) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -func (c *ComponentManager[T]) AllDataParallel(yield func(*T) bool) { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") - - c.components.AllDataParallel(yield) - - assert.True(c.components.Len() == c.lookup.Len(), "Lookup Count must always be the same as the number of components!") - assert.True(c.entities.Len() == c.components.Len(), "Entity Count must always be the same as the number of components!") -} - -// Utils - -func (c *ComponentManager[T]) Len() int32 { - assert.True(c.isInitialized, "ComponentManager should be created with CreateComponentService()") - - return c.components.Len() -} - -func (c *ComponentManager[T]) Clean() { - c.maskComponent.Clean() - // c.components.Clean() - // c.entities.Clean() -} diff --git a/pkg/ecs/ecs.go b/pkg/ecs/ecs.go index 9a906718..397f0d66 100644 --- a/pkg/ecs/ecs.go +++ b/pkg/ecs/ecs.go @@ -6,49 +6,27 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs -import ( - "sync" - "time" -) - -type WorldID uint32 - const ( PREALLOC_DELETED_ENTITIES uint32 = 1 << 10 ) -func CreateWorld(title string) World { - id := generateWorldID() +func NewEntityManager() EntityManager { maskSet := NewSparseSet[ComponentBitArray256, Entity]() - world := World{ - ID: id, - Title: title, - wg: new(sync.WaitGroup), - mx: new(sync.Mutex), + entityManager := EntityManager{ deletedEntityIDs: make([]Entity, 0, PREALLOC_DELETED_ENTITIES), - entityComponentMask: &maskSet, - lastUpdateAt: time.Now(), - lastFixedUpdateAt: time.Now(), components: make(map[ComponentID]AnyComponentManagerPtr), + entityComponentMask: &maskSet, } - return world + return entityManager } func CreateComponentService[T any](id ComponentID) ComponentService[T] { component := ComponentService[T]{ id: id, - managers: make(map[*World]*ComponentManager[T]), + managers: make(map[*EntityManager]*ComponentManager[T]), } return component } - -func CreateSystemService[T AnySystemControllerPtr](controller T, dependsOn ...AnySystemServicePtr) SystemService[T] { - return SystemService[T]{ - initValue: controller, - dependsOn: dependsOn, - instances: make(map[*World]*SystemServiceInstance), - } -} diff --git a/pkg/ecs/entity-manager.go b/pkg/ecs/entity-manager.go new file mode 100644 index 00000000..4c2400e5 --- /dev/null +++ b/pkg/ecs/entity-manager.go @@ -0,0 +1,163 @@ +/* +This Source Code Form is subject to the terms of the Mozilla +Public License, v. 2.0. If a copy of the MPL was not distributed +with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +===-===-===-===-===-===-===-===-===-=== +Donations during this file development: +-===-===-===-===-===-===-===-===-===-=== + +<- MuTaToR Donated 250 RUB +<- Бодрящий член Donated 100 RUB +<- Plambirik Donated 5 000 RUB +<- Бодрящий член Donated 100 RUB +<- MuTaToR Donated 250 RUB +<- ksana_pro Donated 100 RUB +<- Skomich Donated 250 RUB +<- MuTaToR Donated 250 RUB +<- Бодрящий член Donated 100 RUB +<- мой код полная хуйня Donated 251 RUB +<- ksana_pro Donated 100 RUB +<- дубина Donated 250 RUB +<- WoWnik Donated 100 RUB +<- Vorobyan Donated 100 RUB +<- MuTaToR Donated 250 RUB +<- Мандовожка милана Donated 100 RUB +<- ksana_pro Donated 100 RUB +<- Зритель Donated 250 RUB +<- Ричард Donated 100 RUB +<- ksana_pro Donated 100 RUB +<- Ksana_pro Donated 100 RUB +<- Ksana_pro Donated 100 RUB +<- Ksana_pro Donated 100 RUB +<- Монтажер сука Donated 50 RUB + +Thank you for your support! +*/ + +package ecs + +import ( + "fmt" + "sync" + "sync/atomic" +) + +type EntityManager struct { + lastId Entity + size uint32 + + components map[ComponentID]AnyComponentManagerPtr + deletedEntityIDs []Entity + entityComponentMask *SparseSet[ComponentBitArray256, Entity] + mx sync.Mutex + + patch Patch +} + +type Patch []ComponentPatch + +func (w *EntityManager) RegisterComponentServices(componentPtr ...AnyComponentServicePtr) { + for i := 0; i < len(componentPtr); i++ { + w.components[componentPtr[i].getId()] = componentPtr[i].register(w) + } +} + +func (w *EntityManager) Create() Entity { + var newId = w.generateEntityID() + + w.entityComponentMask.Set(newId, ComponentBitArray256{}) + w.size++ + + return newId +} + +func (w *EntityManager) Delete(entity Entity) { + mask := w.entityComponentMask.GetPtr(entity) + if mask == nil { + panic(fmt.Sprintf("Entity %d does not exist", entity)) + } + + for i := range mask.AllSet { + w.components[i].Remove(entity) + } + + w.entityComponentMask.SoftDelete(entity) + w.deletedEntityIDs = append(w.deletedEntityIDs, entity) + w.size-- +} + +func (w *EntityManager) Clean() { + for i := range w.components { + w.components[i].Clean() + } +} + +func (w *EntityManager) Size() uint32 { + return w.size +} + +func (w *EntityManager) LastId() Entity { + return w.lastId +} + +func (w *EntityManager) Destroy() { + w.Clean() +} + +func (w *EntityManager) PatchGet() Patch { + patch := w.patch + + for _, component := range w.components { + if !component.IsTrackingChanges() { + continue + } + + w.patch[component.getId()] = component.PatchGet() + } + + return patch +} + +func (w *EntityManager) PatchApply(patch Patch) { + for _, componentPatch := range patch { + component := w.components[componentPatch.ID] + if component == nil { + panic(fmt.Sprintf("Component %d does not exist", componentPatch.ID)) + } + + if !component.IsTrackingChanges() { + continue + } + + component.PatchApply(componentPatch) + } +} + +func (w *EntityManager) PatchReset() { + for i, component := range w.components { + if component == nil { + panic(fmt.Sprintf("Component %d does not exist", i)) + } + + if !component.IsTrackingChanges() { + continue + } + + component.PatchReset() + } +} + +func (w *EntityManager) init() { + w.patch = make(Patch, len(w.components)) +} + +func (w *EntityManager) generateEntityID() (newId Entity) { + if len(w.deletedEntityIDs) == 0 { + newId = Entity(atomic.AddUint32((*entityType)(&w.lastId), 1)) + } else { + newId = w.deletedEntityIDs[len(w.deletedEntityIDs)-1] + w.deletedEntityIDs = w.deletedEntityIDs[:len(w.deletedEntityIDs)-1] + } + return newId +} diff --git a/pkg/ecs/example.go b/pkg/ecs/example.go index 0cf0989e..018c5295 100644 --- a/pkg/ecs/example.go +++ b/pkg/ecs/example.go @@ -38,11 +38,11 @@ type TransformSystem struct { transform *ComponentManager[Transform] } -func (s *TransformSystem) Init(world *World) { +func (s *TransformSystem) Init(world *EntityManager) { s.transform = transformComponent.GetManager(world) } -func (s *TransformSystem) Destroy(world *World) {} -func (s *TransformSystem) Run(world *World) { +func (s *TransformSystem) Destroy(world *EntityManager) {} +func (s *TransformSystem) Run(world *EntityManager) { s.n++ for _, t := range s.transform.All { t.X += 1 @@ -58,13 +58,13 @@ type BulletSpawnSystem struct { bullet *ComponentManager[Bullet] } -func (s *BulletSpawnSystem) Init(world *World) { +func (s *BulletSpawnSystem) Init(world *EntityManager) { s.bulletSpawner = bulletSpawnerComponent.GetManager(world) s.transform = transformComponent.GetManager(world) s.bullet = bulletComponent.GetManager(world) } -func (s *BulletSpawnSystem) Destroy(world *World) {} -func (s *BulletSpawnSystem) Run(world *World) { +func (s *BulletSpawnSystem) Destroy(world *EntityManager) {} +func (s *BulletSpawnSystem) Run(world *EntityManager) { s.n++ var bulletData Bullet @@ -76,7 +76,7 @@ func (s *BulletSpawnSystem) Run(world *World) { continue } - newBullet := world.CreateEntity("bullet") + newBullet := world.Create() s.transform.Create(newBullet, *tr) s.bullet.Create(newBullet, bulletData) } @@ -86,15 +86,15 @@ type BulletSystem struct { bullet *ComponentManager[Bullet] } -func (s *BulletSystem) Init(world *World) { +func (s *BulletSystem) Init(world *EntityManager) { s.bullet = bulletComponent.GetManager(world) } -func (s *BulletSystem) Destroy(world *World) {} -func (s *BulletSystem) Run(world *World) { +func (s *BulletSystem) Destroy(world *EntityManager) {} +func (s *BulletSystem) Run(world *EntityManager) { for entId, b := range s.bullet.All { b.HP -= 1 if b.HP <= 0 { - world.DestroyEntity(entId) + world.Delete(entId) } } } @@ -104,7 +104,7 @@ type PlayerSpawnSystem struct { transform *ComponentManager[Transform] } -func (s *PlayerSpawnSystem) Init(world *World) { +func (s *PlayerSpawnSystem) Init(world *EntityManager) { s.bulletSpawner = bulletSpawnerComponent.GetManager(world) s.transform = transformComponent.GetManager(world) @@ -114,7 +114,7 @@ func (s *PlayerSpawnSystem) Init(world *World) { var player Entity for i := 0; i < count; i++ { - player = world.CreateEntity("Player") + player = world.Create() s.transform.Create(player, tra) if i%2 == 0 { @@ -122,5 +122,5 @@ func (s *PlayerSpawnSystem) Init(world *World) { } } } -func (s *PlayerSpawnSystem) Destroy(world *World) {} -func (s *PlayerSpawnSystem) Run(world *World) {} +func (s *PlayerSpawnSystem) Destroy(world *EntityManager) {} +func (s *PlayerSpawnSystem) Run(world *EntityManager) {} diff --git a/pkg/ecs/example_test.go b/pkg/ecs/example_test.go index 231a0cb4..900b1461 100644 --- a/pkg/ecs/example_test.go +++ b/pkg/ecs/example_test.go @@ -7,7 +7,7 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs // func BenchmarkSystems(b *testing.B) { -// var world = CreateWorld("Main") +// var world = NewEntityManager("Main") // world.RegisterComponents( // &transformComponent, diff --git a/pkg/ecs/sparse-set.go b/pkg/ecs/sparse-set.go index b6bdb40a..97738034 100644 --- a/pkg/ecs/sparse-set.go +++ b/pkg/ecs/sparse-set.go @@ -6,14 +6,14 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs -type SparseSet[TData any, TKey Entity | ComponentID | WorldID | int] struct { +type SparseSet[TData any, TKey Entity | ComponentID | int] struct { // TODO: refactor map to a slice with using of a deletedSparseElements slice sparse *ChunkMap[int] denseData *ChunkArray[TData] denseIndex *ChunkArray[TKey] } -func NewSparseSet[TData any, TKey Entity | ComponentID | WorldID | int]() SparseSet[TData, TKey] { +func NewSparseSet[TData any, TKey Entity | ComponentID | int]() SparseSet[TData, TKey] { set := SparseSet[TData, TKey]{} set.sparse = NewChunkMap[int](5, 10) set.denseData = NewChunkArray[TData](5, 10) diff --git a/pkg/ecs/system-builder.go b/pkg/ecs/system-builder.go deleted file mode 100644 index 91c66d2c..00000000 --- a/pkg/ecs/system-builder.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package ecs - -type SystemBuilder struct { - world *World - systems *[][]*SystemServiceInstance -} - -func (b *SystemBuilder) Sequential(systems ...AnySystemServicePtr) *SystemBuilder { - for i := range systems { - instance := systems[i].register(b.world) - *b.systems = append(*b.systems, []*SystemServiceInstance{instance}) - instance.controller.Init(b.world) - } - - return b -} - -func (b *SystemBuilder) Parallel(systems ...AnySystemServicePtr) *SystemBuilder { - systemInstances := []*SystemServiceInstance{} - - for i := range systems { - instance := systems[i].register(b.world) - systemInstances = append(systemInstances, instance) - go instance.Run() - } - - *b.systems = append(*b.systems, systemInstances) - return b -} diff --git a/pkg/ecs/system.go b/pkg/ecs/system.go index 3c305dd5..70e9dc6a 100644 --- a/pkg/ecs/system.go +++ b/pkg/ecs/system.go @@ -6,142 +6,15 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package ecs -import "sync" - -type AnySystemPtr[W any] interface { +type AnySystemPtrDeprecated[W any] interface { Init(*W) Run(*W) Destroy(*W) } -type SystemFunctionMethod int - -const ( - systemFunctionInit SystemFunctionMethod = iota - systemFunctionUpdate - SystemFunctionFixedUpdate - systemFunctionDestroy -) - -type AnySystemControllerPtr interface { - Init(*World) - Update(*World) - FixedUpdate(*World) - Destroy(*World) -} - -type AnySystemServicePtr interface { - register(*World) *SystemServiceInstance - registerDependencyFor(*World, AnySystemServicePtr) - getInstance(*World) *SystemServiceInstance -} - -type SystemServiceInstance struct { - dependsOnNum int - controller AnySystemControllerPtr - dependencyFor []*SystemServiceInstance - dependencyWg *sync.WaitGroup - world *World - initChan chan struct{} - updateChan chan struct{} - fixedUpdateChan chan struct{} - destroyChan chan struct{} -} - -func (s *SystemServiceInstance) PrepareWg() { - s.dependencyWg.Add(s.dependsOnNum) -} - -func (s *SystemServiceInstance) WaitForDeps() { - s.dependencyWg.Wait() -} - -func (s *SystemServiceInstance) SendDone() { - for i := range s.dependencyFor { - s.dependencyFor[i].dependencyWg.Done() - } -} - -func (s *SystemServiceInstance) Add(delta int) { - s.dependencyWg.Add(delta) -} - -func (s *SystemServiceInstance) Run() { - controller := s.controller - - controller.Init(s.world) - defer controller.Destroy(s.world) - - shoudDestroy := false - for !shoudDestroy { - select { - case <-s.initChan: - s.WaitForDeps() - controller.Init(s.world) - s.SendDone() - case <-s.updateChan: - s.WaitForDeps() - controller.Update(s.world) - s.SendDone() - case <-s.fixedUpdateChan: - s.WaitForDeps() - controller.FixedUpdate(s.world) - s.SendDone() - case <-s.destroyChan: - s.WaitForDeps() - shoudDestroy = true - s.SendDone() - } - s.world.wg.Done() - } -} - -func (s *SystemServiceInstance) asyncInit() { - s.initChan <- struct{}{} -} - -func (s *SystemServiceInstance) asyncUpdate() { - s.updateChan <- struct{}{} -} - -func (s *SystemServiceInstance) asyncFixedUpdate() { - s.fixedUpdateChan <- struct{}{} -} - -func (s *SystemServiceInstance) asyncDestroy() { - s.destroyChan <- struct{}{} -} - -type SystemService[T AnySystemControllerPtr] struct { - initValue T - dependsOn []AnySystemServicePtr - instances map[*World]*SystemServiceInstance -} - -func (s *SystemService[T]) register(world *World) *SystemServiceInstance { - s.instances[world] = &SystemServiceInstance{ - controller: s.initValue, - dependsOnNum: len(s.dependsOn), - dependencyWg: new(sync.WaitGroup), - world: world, - initChan: make(chan struct{}), - updateChan: make(chan struct{}), - fixedUpdateChan: make(chan struct{}), - destroyChan: make(chan struct{}), - } - - for i := range s.dependsOn { - s.dependsOn[i].registerDependencyFor(world, s) - } - - return s.instances[world] -} - -func (s *SystemService[T]) registerDependencyFor(world *World, dep AnySystemServicePtr) { - instance := s.instances[world] - instance.dependencyFor = append(instance.dependencyFor, dep.getInstance(world)) -} - -func (s *SystemService[T]) getInstance(world *World) *SystemServiceInstance { - return s.instances[world] +type AnySystemPointer interface { + Init(manager *EntityManager) + Update(manager *EntityManager) + FixedUpdate(manager *EntityManager) + Destroy(manager *EntityManager) } diff --git a/pkg/ecs/world.go b/pkg/ecs/world.go deleted file mode 100644 index b2cd448c..00000000 --- a/pkg/ecs/world.go +++ /dev/null @@ -1,294 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - -===-===-===-===-===-===-===-===-===-=== -Donations during this file development: --===-===-===-===-===-===-===-===-===-=== - -<- MuTaToR Donated 250 RUB -<- Бодрящий член Donated 100 RUB -<- Plambirik Donated 5 000 RUB -<- Бодрящий член Donated 100 RUB -<- MuTaToR Donated 250 RUB -<- ksana_pro Donated 100 RUB -<- Skomich Donated 250 RUB -<- MuTaToR Donated 250 RUB -<- Бодрящий член Donated 100 RUB -<- мой код полная хуйня Donated 251 RUB -<- ksana_pro Donated 100 RUB -<- дубина Donated 250 RUB -<- WoWnik Donated 100 RUB -<- Vorobyan Donated 100 RUB -<- MuTaToR Donated 250 RUB -<- Мандовожка милана Donated 100 RUB -<- ksana_pro Donated 100 RUB -<- Зритель Donated 250 RUB -<- Ричард Donated 100 RUB -<- ksana_pro Donated 100 RUB -<- Ksana_pro Donated 100 RUB -<- Ksana_pro Donated 100 RUB -<- Ksana_pro Donated 100 RUB -<- Монтажер сука Donated 50 RUB - -Thank you for your support! -*/ - -package ecs - -import ( - "fmt" - "sync" - "sync/atomic" - "time" -) - -func init() { - nextWorldId.Store(-1) -} - -var nextWorldId atomic.Int32 - -func generateWorldID() WorldID { - id := nextWorldId.Add(1) - return WorldID(id) -} - -type World struct { - ID WorldID - Title string - - tick int - lastEntityID Entity - - size uint32 - shouldDestroy bool - - systems [][]*SystemServiceInstance - components map[ComponentID]AnyComponentManagerPtr - deletedEntityIDs []Entity - entityComponentMask *SparseSet[ComponentBitArray256, Entity] - wg *sync.WaitGroup - mx *sync.Mutex - - patch WorldPatch - - lastUpdateAt time.Time - lastFixedUpdateAt time.Time -} - -type WorldPatch []ComponentPatch - -func (w *World) RegisterComponentServices(componentPtr ...AnyComponentServicePtr) { - for i := 0; i < len(componentPtr); i++ { - w.components[componentPtr[i].getId()] = componentPtr[i].register(w) - } -} - -func (w *World) RegisterSystems() *SystemBuilder { - return &SystemBuilder{ - world: w, - systems: &w.systems, - } -} - -func (w *World) FixedUpdate() error { - w.runSystemFunction(SystemFunctionFixedUpdate) - return nil -} - -func (w *World) runSystemFunction(method SystemFunctionMethod) error { - for i := range w.systems { - parallel := w.systems[i] - - if len(parallel) == 1 { - controller := parallel[0].controller - switch method { - case systemFunctionInit: - controller.Init(w) - case systemFunctionUpdate: - controller.Update(w) - case SystemFunctionFixedUpdate: - controller.FixedUpdate(w) - case systemFunctionDestroy: - controller.Destroy(w) - } - continue - } - - for j := range parallel { - parallel[j].PrepareWg() - } - - w.wg.Add(len(parallel)) - for j := range parallel { - controller := parallel[j] - - switch method { - case systemFunctionInit: - controller.asyncInit() - case systemFunctionUpdate: - controller.asyncUpdate() - case SystemFunctionFixedUpdate: - controller.asyncFixedUpdate() - case systemFunctionDestroy: - controller.asyncDestroy() - } - } - w.wg.Wait() - } - - if method == SystemFunctionFixedUpdate { - w.tick++ - } - - w.Clean() - - return nil -} - -func (w *World) CreateEntity(title string) Entity { - var newId = w.generateEntityID() - - w.entityComponentMask.Set(newId, ComponentBitArray256{}) - w.size++ - - return newId -} - -func (w *World) DestroyEntity(entityId Entity) { - mask := w.entityComponentMask.GetPtr(entityId) - if mask == nil { - panic(fmt.Sprintf("Entity %d does not exist", entityId)) - } - - for i := range mask.AllSet { - w.components[i].Remove(entityId) - } - - w.entityComponentMask.SoftDelete(entityId) - w.deletedEntityIDs = append(w.deletedEntityIDs, entityId) - w.size-- -} - -func (w *World) Clean() { - for i := range w.components { - w.components[i].Clean() - } -} - -func (w *World) Size() uint32 { - return w.size -} - -func (w *World) LastEntityID() Entity { - return w.lastEntityID -} - -func (w *World) ShouldDestroy() bool { - return w.shouldDestroy -} - -func (w *World) SetShouldDestroy(value bool) { - w.shouldDestroy = value -} - -func (w *World) Destroy() { - w.runSystemFunction(systemFunctionDestroy) - w.wg.Wait() - w.Clean() -} - -func (w *World) PatchGet() WorldPatch { - patch := w.patch - - for _, component := range w.components { - if !component.IsTrackingChanges() { - continue - } - - w.patch[component.getId()] = component.PatchGet() - } - - return patch -} - -func (w *World) PatchApply(patch WorldPatch) { - for _, componentPatch := range patch { - component := w.components[componentPatch.ID] - if component == nil { - panic(fmt.Sprintf("Component %d does not exist", componentPatch.ID)) - } - - if !component.IsTrackingChanges() { - continue - } - - component.PatchApply(componentPatch) - } -} - -func (w *World) PatchReset() { - for i, component := range w.components { - if component == nil { - panic(fmt.Sprintf("Component %d does not exist", i)) - } - - if !component.IsTrackingChanges() { - continue - } - - component.PatchReset() - } -} - -func (w *World) init() { - w.patch = make(WorldPatch, len(w.components)) -} - -func (w *World) Run(tickrate uint) { - w.init() - - duration := time.Second / time.Duration(tickrate) - - ticker := time.NewTicker(duration) - defer ticker.Stop() - - var t time.Time - - for !w.ShouldDestroy() { - needFixedUpdate := true - for needFixedUpdate { - select { - default: - needFixedUpdate = false - case <-ticker.C: - t = time.Now() - w.runSystemFunction(SystemFunctionFixedUpdate) - w.lastFixedUpdateAt = t - } - } - t = time.Now() - w.runSystemFunction(systemFunctionUpdate) - w.lastUpdateAt = t - } -} - -func (w *World) DtUpdate() time.Duration { - return time.Since(w.lastUpdateAt) -} - -func (w *World) DtFixedUpdate() time.Duration { - return time.Since(w.lastFixedUpdateAt) -} - -func (w *World) generateEntityID() (newId Entity) { - if len(w.deletedEntityIDs) == 0 { - newId = Entity(atomic.AddUint32((*entityType)(&w.lastEntityID), 1)) - } else { - newId = w.deletedEntityIDs[len(w.deletedEntityIDs)-1] - w.deletedEntityIDs = w.deletedEntityIDs[:len(w.deletedEntityIDs)-1] - } - return newId -} diff --git a/stdcomponents/animation-player.go b/stdcomponents/animation-player.go index b06b7e09..b4529ad9 100644 --- a/stdcomponents/animation-player.go +++ b/stdcomponents/animation-player.go @@ -34,6 +34,6 @@ type AnimationPlayer struct { type AnimationPlayerComponentManager = ecs.ComponentManager[AnimationPlayer] -func NewAnimationPlayerComponentManager(world *ecs.World) AnimationPlayerComponentManager { +func NewAnimationPlayerComponentManager(world *ecs.EntityManager) AnimationPlayerComponentManager { return ecs.NewComponentManager[AnimationPlayer](world, ANIMATION_PLAYER_ID) } diff --git a/stdcomponents/animation-state.go b/stdcomponents/animation-state.go index 49a940f4..32296703 100644 --- a/stdcomponents/animation-state.go +++ b/stdcomponents/animation-state.go @@ -22,6 +22,6 @@ type AnimationState int32 type AnimationStateComponentManager = ecs.ComponentManager[AnimationState] -func NewAnimationStateComponentManager(world *ecs.World) AnimationStateComponentManager { +func NewAnimationStateComponentManager(world *ecs.EntityManager) AnimationStateComponentManager { return ecs.NewComponentManager[AnimationState](world, ANIMATION_STATE_ID) } diff --git a/stdcomponents/flip.go b/stdcomponents/flip.go index 7edf28db..12afbe00 100644 --- a/stdcomponents/flip.go +++ b/stdcomponents/flip.go @@ -22,6 +22,6 @@ type Flip struct { type FlipComponentManager = ecs.ComponentManager[Flip] -func NewFlipComponentManager(world *ecs.World) FlipComponentManager { +func NewFlipComponentManager(world *ecs.EntityManager) FlipComponentManager { return ecs.NewComponentManager[Flip](world, FLIP_ID) } diff --git a/stdcomponents/network.go b/stdcomponents/network.go index 9a7fc5bf..aa30a39e 100644 --- a/stdcomponents/network.go +++ b/stdcomponents/network.go @@ -25,6 +25,6 @@ type Network struct { type NetworkComponentManager = ecs.ComponentManager[Network] -func NewNetworkComponentManager(world *ecs.World) NetworkComponentManager { +func NewNetworkComponentManager(world *ecs.EntityManager) NetworkComponentManager { return ecs.NewComponentManager[Network](world, NETWORK_ID) } diff --git a/stdcomponents/position.go b/stdcomponents/position.go index 24b94b81..ea65c26b 100644 --- a/stdcomponents/position.go +++ b/stdcomponents/position.go @@ -22,6 +22,6 @@ type Position struct { type PositionComponentManager = ecs.ComponentManager[Position] -func NewPositionComponentManager(world *ecs.World) PositionComponentManager { +func NewPositionComponentManager(world *ecs.EntityManager) PositionComponentManager { return ecs.NewComponentManager[Position](world, POSITION_ID) } diff --git a/stdcomponents/rotation.go b/stdcomponents/rotation.go index 8e707164..e7997dd0 100644 --- a/stdcomponents/rotation.go +++ b/stdcomponents/rotation.go @@ -22,6 +22,6 @@ type Rotation struct { type RotationComponentManager = ecs.ComponentManager[Rotation] -func NewRotationComponentManager(world *ecs.World) RotationComponentManager { +func NewRotationComponentManager(world *ecs.EntityManager) RotationComponentManager { return ecs.NewComponentManager[Rotation](world, ROTATION_ID) } diff --git a/stdcomponents/scale.go b/stdcomponents/scale.go index 28cb7b1a..c6450e70 100644 --- a/stdcomponents/scale.go +++ b/stdcomponents/scale.go @@ -22,6 +22,6 @@ type Scale struct { type ScaleComponentManager = ecs.ComponentManager[Scale] -func NewScaleComponentManager(world *ecs.World) ScaleComponentManager { +func NewScaleComponentManager(world *ecs.EntityManager) ScaleComponentManager { return ecs.NewComponentManager[Scale](world, SCALE_ID) } diff --git a/stdcomponents/sprite-matrix.go b/stdcomponents/sprite-matrix.go index d83d6044..2d8d9d03 100644 --- a/stdcomponents/sprite-matrix.go +++ b/stdcomponents/sprite-matrix.go @@ -36,6 +36,6 @@ type SpriteMatrix struct { type SpriteMatrixComponentManager = ecs.ComponentManager[SpriteMatrix] -func NewSpriteMatrixComponentManager(world *ecs.World) SpriteMatrixComponentManager { +func NewSpriteMatrixComponentManager(world *ecs.EntityManager) SpriteMatrixComponentManager { return ecs.NewComponentManager[SpriteMatrix](world, SPRITE_MATRIX_ID) } diff --git a/stdcomponents/sprite-sheet.go b/stdcomponents/sprite-sheet.go index 87363023..9c53a17a 100644 --- a/stdcomponents/sprite-sheet.go +++ b/stdcomponents/sprite-sheet.go @@ -30,6 +30,6 @@ type SpriteSheet struct { type SpriteSheetComponentManager = ecs.ComponentManager[SpriteSheet] -func NewSpriteSheetComponentManager(world *ecs.World) SpriteSheetComponentManager { +func NewSpriteSheetComponentManager(world *ecs.EntityManager) SpriteSheetComponentManager { return ecs.NewComponentManager[SpriteSheet](world, SPRITE_SHEET_ID) } diff --git a/stdcomponents/sprite.go b/stdcomponents/sprite.go index 70250a29..e378d96c 100644 --- a/stdcomponents/sprite.go +++ b/stdcomponents/sprite.go @@ -29,6 +29,6 @@ type Sprite struct { type SpriteComponentManager = ecs.ComponentManager[Sprite] -func NewSpriteComponentManager(world *ecs.World) SpriteComponentManager { +func NewSpriteComponentManager(world *ecs.EntityManager) SpriteComponentManager { return ecs.NewComponentManager[Sprite](world, SPRITE_ID) } diff --git a/stdcomponents/texture-render.go b/stdcomponents/texture-render.go index 98b926b7..5c2fcf6a 100644 --- a/stdcomponents/texture-render.go +++ b/stdcomponents/texture-render.go @@ -31,6 +31,6 @@ type TextureRender struct { type TextureRenderComponentManager = ecs.ComponentManager[TextureRender] -func NewTextureRenderComponentManager(world *ecs.World) TextureRenderComponentManager { +func NewTextureRenderComponentManager(world *ecs.EntityManager) TextureRenderComponentManager { return ecs.NewComponentManager[TextureRender](world, TEXTURE_RENDER_ID) } diff --git a/stdcomponents/tint.go b/stdcomponents/tint.go index 4703cb05..75bd45c5 100644 --- a/stdcomponents/tint.go +++ b/stdcomponents/tint.go @@ -23,6 +23,6 @@ type Tint = color.RGBA type TintComponentManager = ecs.ComponentManager[Tint] -func NewTintComponentManager(world *ecs.World) TintComponentManager { +func NewTintComponentManager(world *ecs.EntityManager) TintComponentManager { return ecs.NewComponentManager[Tint](world, TINT_ID) } diff --git a/stdcomponents/velocity.go b/stdcomponents/velocity.go index c99dd48a..f4a24bce 100644 --- a/stdcomponents/velocity.go +++ b/stdcomponents/velocity.go @@ -22,6 +22,6 @@ type Velocity struct { type VelocityComponentManager = ecs.ComponentManager[Velocity] -func NewVelocityComponentManager(world *ecs.World) VelocityComponentManager { +func NewVelocityComponentManager(world *ecs.EntityManager) VelocityComponentManager { return ecs.NewComponentManager[Velocity](world, VELOCITY_ID) } diff --git a/stdsystems/animation-spritematrix.go b/stdsystems/animation-spritematrix.go index a1052d86..f328c3fa 100644 --- a/stdsystems/animation-spritematrix.go +++ b/stdsystems/animation-spritematrix.go @@ -17,7 +17,7 @@ func NewAnimationSpriteMatrixSystem() AnimationSpriteMatrixSystem { } type AnimationSpriteMatrixSystem struct { - World *ecs.World + World *ecs.EntityManager AnimationPlayers *ecs.ComponentManager[stdcomponents.AnimationPlayer] AnimationStates *ecs.ComponentManager[stdcomponents.AnimationState] SpriteMatrixes *ecs.ComponentManager[stdcomponents.SpriteMatrix] diff --git a/stdsystems/asset-library.go b/stdsystems/asset-library.go index f45a716a..6052ac2d 100644 --- a/stdsystems/asset-library.go +++ b/stdsystems/asset-library.go @@ -7,18 +7,18 @@ with this file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdsystems import ( - "gomp/pkg/ecs" + "gomp" "time" ) -func NewAssetLibSystem(assets []ecs.AnyAssetLibrary) AssetLibSystem { +func NewAssetLibSystem(assets []gomp.AnyAssetLibrary) AssetLibSystem { return AssetLibSystem{ assets: assets, } } type AssetLibSystem struct { - assets []ecs.AnyAssetLibrary + assets []gomp.AnyAssetLibrary } func (s *AssetLibSystem) Init() {} diff --git a/stdsystems/network-send.go b/stdsystems/network-send.go index 20a5c536..d32f6001 100644 --- a/stdsystems/network-send.go +++ b/stdsystems/network-send.go @@ -27,7 +27,7 @@ func NewNetworkSendSystem() NetworkSendSystem { } type NetworkSendSystem struct { - World *ecs.World + World *ecs.EntityManager Positions *stdcomponents.PositionComponentManager Rotations *stdcomponents.RotationComponentManager Mirroreds *stdcomponents.FlipComponentManager diff --git a/stdsystems/render.go b/stdsystems/render.go index c55273cb..6f526c71 100644 --- a/stdsystems/render.go +++ b/stdsystems/render.go @@ -19,7 +19,7 @@ func NewRenderSystem() RenderSystem { } type RenderSystem struct { - World *ecs.World + World *ecs.EntityManager TextureRenders *ecs.ComponentManager[stdcomponents.TextureRender] } @@ -47,7 +47,6 @@ func (s *RenderSystem) Run(dt time.Duration) bool { rl.DrawFPS(10, 10) rl.DrawText(fmt.Sprintf("%d", s.World.Size()), 10, 30, 20, rl.Red) rl.DrawText(fmt.Sprintf("%s", dt), 10, 50, 20, rl.Red) - rl.DrawText(fmt.Sprintf("%s", s.World.DtFixedUpdate()), 10, 70, 20, rl.Red) rl.EndDrawing() return true From 962e6e228bbc3ba464bb9dd46d2d815a997f01c5 Mon Sep 17 00:00:00 2001 From: milanjrodd Date: Fri, 21 Feb 2025 20:11:04 +0300 Subject: [PATCH 014/196] feat new api --- desktop-components.go | 29 ++- examples/ebiten-ecs-370k/camera-system.go | 114 ---------- examples/ebiten-ecs-370k/color-system.go | 48 ----- examples/ebiten-ecs-370k/components.go | 62 ------ examples/ebiten-ecs-370k/destroy-system.go | 41 ---- examples/ebiten-ecs-370k/game.go | 82 ------- examples/ebiten-ecs-370k/hp-system.go | 40 ---- examples/ebiten-ecs-370k/main.go | 34 --- examples/ebiten-ecs-370k/spawn-system.go | 105 --------- examples/ebiten-ecs/client.go | 40 ---- .../components/hero-intent-component.go | 21 -- examples/ebiten-ecs/entities/Hero.go | 32 --- examples/ebiten-ecs/resources/resources.go | 17 -- .../ebiten-ecs/resources/sprites/area.png | Bin 168 -> 0 bytes .../sprites/big_demon_idle_anim_f0.png | Bin 481 -> 0 bytes .../sprites/big_demon_idle_anim_f1.png | Bin 497 -> 0 bytes .../sprites/big_demon_idle_anim_f2.png | Bin 483 -> 0 bytes .../sprites/big_demon_idle_anim_f3.png | Bin 468 -> 0 bytes .../sprites/big_demon_run_anim_f0.png | Bin 466 -> 0 bytes .../sprites/big_demon_run_anim_f1.png | Bin 503 -> 0 bytes .../sprites/big_demon_run_anim_f2.png | Bin 518 -> 0 bytes .../sprites/big_demon_run_anim_f3.png | Bin 486 -> 0 bytes .../sprites/big_zombie_idle_anim_f0.png | Bin 475 -> 0 bytes .../sprites/big_zombie_idle_anim_f1.png | Bin 461 -> 0 bytes .../sprites/big_zombie_idle_anim_f2.png | Bin 458 -> 0 bytes .../sprites/big_zombie_idle_anim_f3.png | Bin 430 -> 0 bytes .../sprites/big_zombie_run_anim_f0.png | Bin 447 -> 0 bytes .../sprites/big_zombie_run_anim_f1.png | Bin 499 -> 0 bytes .../sprites/big_zombie_run_anim_f2.png | Bin 515 -> 0 bytes .../sprites/big_zombie_run_anim_f3.png | Bin 496 -> 0 bytes examples/ebiten-ecs/scenes/dungeon.go | 9 - examples/ebiten-ecs/scenes/village.go | 11 - examples/ebiten-ecs/server.go | 16 -- .../ebiten-ecs/systems/hero-move-system.go | 41 ---- examples/ebiten-ecs/systems/intent-system.go | 77 ------- examples/new-api/components/hp.go | 4 +- examples/new-api/components/ids.go | 2 +- examples/new-api/game.go | 4 +- examples/new-api/instances/component-list.go | 31 ++- examples/new-api/instances/system-list.go | 54 +---- .../instances/world.go} | 9 +- examples/new-api/scenes/main-scene.go | 138 ++++++------ .../scenes/scene-list.go} | 19 +- examples/new-api/systems/hp.go | 31 --- examples/new-api/systems/player.go | 13 +- examples/raylib-ecs/assets/assets.go | 20 -- examples/raylib-ecs/assets/milansheet.png | Bin 96455 -> 0 bytes examples/raylib-ecs/assets/star.png | Bin 160 -> 0 bytes examples/raylib-ecs/components/components.go | 42 ---- examples/raylib-ecs/components/ids.go | 31 --- examples/raylib-ecs/components/types.go | 103 --------- examples/raylib-ecs/entities/player.go | 116 ---------- examples/raylib-ecs/main.go | 84 -------- .../raylib-ecs/systems/animation-player.go | 62 ------ .../systems/animation-spritematrix.go | 55 ----- examples/raylib-ecs/systems/asset-library.go | 29 --- examples/raylib-ecs/systems/color.go | 44 ---- examples/raylib-ecs/systems/debug.go | 42 ---- examples/raylib-ecs/systems/hp.go | 31 --- examples/raylib-ecs/systems/init.go | 20 -- .../raylib-ecs/systems/network-receive.go | 26 --- examples/raylib-ecs/systems/network-send.go | 72 ------- examples/raylib-ecs/systems/network.go | 46 ---- examples/raylib-ecs/systems/player.go | 56 ----- examples/raylib-ecs/systems/render.go | 55 ----- examples/raylib-ecs/systems/spawn.go | 89 -------- examples/raylib-ecs/systems/systems-engine.go | 56 ----- .../systems/texture-render-animation.go | 45 ---- .../systems/texture-render-mirrored.go | 45 ---- .../systems/texture-render-position.go | 41 ---- .../systems/texture-render-rotation.go | 40 ---- .../systems/texture-render-scale.go | 41 ---- .../systems/texture-render-sprite.go | 86 -------- .../systems/texture-render-spritematrix.go | 66 ------ .../systems/texture-render-spritesheet.go | 56 ----- .../raylib-ecs/systems/texture-render-tint.go | 44 ---- go.mod | 2 +- internal/tomb-mates-demo/main.go | 4 +- pkg/ecs/bit-array.go | 14 +- pkg/ecs/bit-set.go | 144 +++++++++++++ pkg/ecs/component-manager.go | 38 ++-- pkg/ecs/component-service.go | 64 ------ pkg/ecs/ecs.go | 25 --- pkg/ecs/entity-manager.go | 107 ++++----- pkg/ecs/{types.go => entity.go} | 21 +- pkg/ecs/example.go | 126 ----------- pkg/ecs/example_test.go | 172 --------------- pkg/ecs/sparse-set.go | 4 +- pkg/ecs/system.go | 5 +- pkg/ecs/world.go | 108 ++++++++++ pkg/gomp/basic-components.client.go | 14 -- pkg/gomp/basic-components.go | 16 -- pkg/gomp/body-system.go | 57 ----- pkg/gomp/ebiten.go | 74 ------- pkg/gomp/ecs-component.go | 10 - pkg/gomp/ecs-entitiy.go | 5 - pkg/gomp/ecs-system.go | 25 --- pkg/gomp/ecs-world.go | 5 - pkg/gomp/game-client.go | 44 ---- pkg/gomp/game.go | 169 --------------- pkg/gomp/gomp.go | 92 -------- pkg/gomp/network-player.go | 15 -- pkg/gomp/network-system.go | 74 ------- pkg/gomp/render-component-ebiten.go | 17 -- pkg/gomp/render-system-ebiten.go | 34 --- pkg/gomp/resources.go | 99 --------- pkg/gomp/scene-system.go | 16 -- pkg/gomp/scene.go | 45 ---- pkg/gomp/server.go | 203 ------------------ pkg/legacy/network-system.go | 74 +++++++ pkg/{gomp => legacy}/network.go | 2 +- pkg/legacy/resources.go | 99 +++++++++ pkg/legacy/server.go | 203 ++++++++++++++++++ stdcomponents/animation-player.go | 4 +- stdcomponents/animation-state.go | 4 +- stdcomponents/flip.go | 4 +- stdcomponents/ids.go | 2 +- stdcomponents/network.go | 4 +- stdcomponents/position.go | 4 +- stdcomponents/rotation.go | 4 +- stdcomponents/scale.go | 4 +- stdcomponents/sprite-matrix.go | 4 +- stdcomponents/sprite-sheet.go | 4 +- stdcomponents/sprite.go | 4 +- stdcomponents/texture-render.go | 4 +- stdcomponents/tint.go | 4 +- stdcomponents/velocity.go | 4 +- stdsystems/debug.go | 5 +- stdsystems/render.go | 14 +- 129 files changed, 902 insertions(+), 4064 deletions(-) delete mode 100644 examples/ebiten-ecs-370k/camera-system.go delete mode 100644 examples/ebiten-ecs-370k/color-system.go delete mode 100644 examples/ebiten-ecs-370k/components.go delete mode 100644 examples/ebiten-ecs-370k/destroy-system.go delete mode 100644 examples/ebiten-ecs-370k/game.go delete mode 100644 examples/ebiten-ecs-370k/hp-system.go delete mode 100644 examples/ebiten-ecs-370k/main.go delete mode 100644 examples/ebiten-ecs-370k/spawn-system.go delete mode 100644 examples/ebiten-ecs/client.go delete mode 100644 examples/ebiten-ecs/components/hero-intent-component.go delete mode 100644 examples/ebiten-ecs/entities/Hero.go delete mode 100644 examples/ebiten-ecs/resources/resources.go delete mode 100644 examples/ebiten-ecs/resources/sprites/area.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_demon_idle_anim_f0.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_demon_idle_anim_f1.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_demon_idle_anim_f2.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_demon_idle_anim_f3.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f0.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f1.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f2.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f3.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_zombie_idle_anim_f0.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_zombie_idle_anim_f1.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_zombie_idle_anim_f2.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_zombie_idle_anim_f3.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f0.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f1.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f2.png delete mode 100644 examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f3.png delete mode 100644 examples/ebiten-ecs/scenes/dungeon.go delete mode 100644 examples/ebiten-ecs/scenes/village.go delete mode 100644 examples/ebiten-ecs/server.go delete mode 100644 examples/ebiten-ecs/systems/hero-move-system.go delete mode 100644 examples/ebiten-ecs/systems/intent-system.go rename examples/{raylib-ecs/systems/systems-business.go => new-api/instances/world.go} (54%) rename examples/{raylib-ecs/systems/example.go => new-api/scenes/scene-list.go} (50%) delete mode 100644 examples/new-api/systems/hp.go delete mode 100644 examples/raylib-ecs/assets/assets.go delete mode 100644 examples/raylib-ecs/assets/milansheet.png delete mode 100644 examples/raylib-ecs/assets/star.png delete mode 100644 examples/raylib-ecs/components/components.go delete mode 100644 examples/raylib-ecs/components/ids.go delete mode 100644 examples/raylib-ecs/components/types.go delete mode 100644 examples/raylib-ecs/entities/player.go delete mode 100644 examples/raylib-ecs/main.go delete mode 100644 examples/raylib-ecs/systems/animation-player.go delete mode 100644 examples/raylib-ecs/systems/animation-spritematrix.go delete mode 100644 examples/raylib-ecs/systems/asset-library.go delete mode 100644 examples/raylib-ecs/systems/color.go delete mode 100644 examples/raylib-ecs/systems/debug.go delete mode 100644 examples/raylib-ecs/systems/hp.go delete mode 100644 examples/raylib-ecs/systems/init.go delete mode 100644 examples/raylib-ecs/systems/network-receive.go delete mode 100644 examples/raylib-ecs/systems/network-send.go delete mode 100644 examples/raylib-ecs/systems/network.go delete mode 100644 examples/raylib-ecs/systems/player.go delete mode 100644 examples/raylib-ecs/systems/render.go delete mode 100644 examples/raylib-ecs/systems/spawn.go delete mode 100644 examples/raylib-ecs/systems/systems-engine.go delete mode 100644 examples/raylib-ecs/systems/texture-render-animation.go delete mode 100644 examples/raylib-ecs/systems/texture-render-mirrored.go delete mode 100644 examples/raylib-ecs/systems/texture-render-position.go delete mode 100644 examples/raylib-ecs/systems/texture-render-rotation.go delete mode 100644 examples/raylib-ecs/systems/texture-render-scale.go delete mode 100644 examples/raylib-ecs/systems/texture-render-sprite.go delete mode 100644 examples/raylib-ecs/systems/texture-render-spritematrix.go delete mode 100644 examples/raylib-ecs/systems/texture-render-spritesheet.go delete mode 100644 examples/raylib-ecs/systems/texture-render-tint.go create mode 100644 pkg/ecs/bit-set.go delete mode 100644 pkg/ecs/component-service.go rename pkg/ecs/{types.go => entity.go} (74%) delete mode 100644 pkg/ecs/example.go delete mode 100644 pkg/ecs/example_test.go create mode 100644 pkg/ecs/world.go delete mode 100644 pkg/gomp/basic-components.client.go delete mode 100644 pkg/gomp/basic-components.go delete mode 100644 pkg/gomp/body-system.go delete mode 100644 pkg/gomp/ebiten.go delete mode 100644 pkg/gomp/ecs-component.go delete mode 100644 pkg/gomp/ecs-entitiy.go delete mode 100644 pkg/gomp/ecs-system.go delete mode 100644 pkg/gomp/ecs-world.go delete mode 100644 pkg/gomp/game-client.go delete mode 100644 pkg/gomp/game.go delete mode 100644 pkg/gomp/gomp.go delete mode 100644 pkg/gomp/network-player.go delete mode 100644 pkg/gomp/network-system.go delete mode 100644 pkg/gomp/render-component-ebiten.go delete mode 100644 pkg/gomp/render-system-ebiten.go delete mode 100644 pkg/gomp/resources.go delete mode 100644 pkg/gomp/scene-system.go delete mode 100644 pkg/gomp/scene.go delete mode 100644 pkg/gomp/server.go create mode 100644 pkg/legacy/network-system.go rename pkg/{gomp => legacy}/network.go (99%) create mode 100644 pkg/legacy/resources.go create mode 100644 pkg/legacy/server.go diff --git a/desktop-components.go b/desktop-components.go index f4a4f25b..b726cfd8 100644 --- a/desktop-components.go +++ b/desktop-components.go @@ -15,25 +15,24 @@ Thank you for your support! package gomp import ( - "gomp/pkg/ecs" "gomp/stdcomponents" ) -func NewDesktopComponents(world *ecs.EntityManager) DesktopComponents { +func NewDesktopComponents() DesktopComponents { return DesktopComponents{ - Position: stdcomponents.NewPositionComponentManager(world), - Rotation: stdcomponents.NewRotationComponentManager(world), - Scale: stdcomponents.NewScaleComponentManager(world), - Velocity: stdcomponents.NewVelocityComponentManager(world), - Flip: stdcomponents.NewFlipComponentManager(world), - Sprite: stdcomponents.NewSpriteComponentManager(world), - SpriteSheet: stdcomponents.NewSpriteSheetComponentManager(world), - SpriteMatrix: stdcomponents.NewSpriteMatrixComponentManager(world), - Tint: stdcomponents.NewTintComponentManager(world), - AnimationPlayer: stdcomponents.NewAnimationPlayerComponentManager(world), - AnimationState: stdcomponents.NewAnimationStateComponentManager(world), - TextureRender: stdcomponents.NewTextureRenderComponentManager(world), - Network: stdcomponents.NewNetworkComponentManager(world), + Position: stdcomponents.NewPositionComponentManager(), + Rotation: stdcomponents.NewRotationComponentManager(), + Scale: stdcomponents.NewScaleComponentManager(), + Velocity: stdcomponents.NewVelocityComponentManager(), + Flip: stdcomponents.NewFlipComponentManager(), + Sprite: stdcomponents.NewSpriteComponentManager(), + SpriteSheet: stdcomponents.NewSpriteSheetComponentManager(), + SpriteMatrix: stdcomponents.NewSpriteMatrixComponentManager(), + Tint: stdcomponents.NewTintComponentManager(), + AnimationPlayer: stdcomponents.NewAnimationPlayerComponentManager(), + AnimationState: stdcomponents.NewAnimationStateComponentManager(), + TextureRender: stdcomponents.NewTextureRenderComponentManager(), + Network: stdcomponents.NewNetworkComponentManager(), } } diff --git a/examples/ebiten-ecs-370k/camera-system.go b/examples/ebiten-ecs-370k/camera-system.go deleted file mode 100644 index af71dd0d..00000000 --- a/examples/ebiten-ecs-370k/camera-system.go +++ /dev/null @@ -1,114 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package main - -import ( - "fmt" - ecs2 "gomp/pkg/ecs" - "image" - "image/color" - "image/draw" - "strings" - - "github.com/hajimehoshi/ebiten/v2" - "github.com/hajimehoshi/ebiten/v2/ebitenutil" - "golang.org/x/text/language" - "golang.org/x/text/message" -) - -type cameraSystem struct { - transformComponent *ecs2.ComponentManager[transform] - healthComponent *ecs2.ComponentManager[health] - colorComponent *ecs2.ComponentManager[color.RGBA] - movementComponent *ecs2.ComponentManager[movement] - cameraComponent *ecs2.ComponentManager[camera] - destroyComponentType *ecs2.ComponentManager[empty] - - buffer *image.RGBA - debugInfo []string - p *message.Printer -} - -func (s *cameraSystem) Init(world *ecs2.EntityManager) { - s.transformComponent = transformComponentType.GetManager(world) - s.healthComponent = healthComponentType.GetManager(world) - s.colorComponent = colorComponentType.GetManager(world) - s.movementComponent = movementComponentType.GetManager(world) - s.cameraComponent = cameraComponentType.GetManager(world) - s.destroyComponentType = destroyComponentType.GetManager(world) - - s.p = message.NewPrinter(language.Russian) - - newcamera := world.Create() - s.cameraComponent.Create(newcamera, camera{ - mainLayer: cameraLayer{ - image: ebiten.NewImage(width, height), - zoom: 1, - }, - debugLayer: cameraLayer{ - image: ebiten.NewImage(width, height), - zoom: 2, - }, - }) - - s.buffer = image.NewRGBA(image.Rect(0, 0, width, height)) - s.debugInfo = make([]string, 0) -} - -func (s *cameraSystem) Run(world *ecs2.EntityManager) { - _, dy := ebiten.Wheel() - - draw.Draw(s.buffer, s.buffer.Bounds(), &image.Uniform{color.Transparent}, image.Point{}, draw.Src) - - s.colorComponent.AllParallel(func(entity ecs2.Entity, color *color.RGBA) bool { - if color == nil { - return true - } - - transform := s.transformComponent.Get(entity) - if transform == nil { - return true - } - - s.buffer.SetRGBA(int(transform.x), int(transform.y), *color) - return true - }) - - var mainCamera *camera - s.cameraComponent.AllData(func(c *camera) bool { - mainCamera = c - return false - }) - - mainCamera.mainLayer.image.Clear() - mainCamera.debugLayer.image.Clear() - - mainCamera.mainLayer.zoom += float64(dy) - mainCamera.mainLayer.image.WritePixels(s.buffer.Pix) - - if mainCamera.mainLayer.zoom < 0.1 { - mainCamera.mainLayer.zoom = 0.1 - } else if mainCamera.mainLayer.zoom > 100 { - mainCamera.mainLayer.zoom = 100 - } - - s.debugInfo = append(s.debugInfo, fmt.Sprintf("TPS %0.2f", ebiten.ActualTPS())) - s.debugInfo = append(s.debugInfo, fmt.Sprintf("FPS %0.2f", ebiten.ActualFPS())) - s.debugInfo = append(s.debugInfo, fmt.Sprintf("Zoom %0.2f", mainCamera.mainLayer.zoom)) - s.debugInfo = append(s.debugInfo, s.p.Sprintf("Entity count %d", entityCount)) - s.debugInfo = append(s.debugInfo, s.p.Sprintf("Transforms count %d", s.transformComponent.Len())) - s.debugInfo = append(s.debugInfo, s.p.Sprintf("Healths count %d", s.healthComponent.Len())) - s.debugInfo = append(s.debugInfo, s.p.Sprintf("Colors count %d", s.colorComponent.Len())) - s.debugInfo = append(s.debugInfo, s.p.Sprintf("Movements count %d", s.movementComponent.Len())) - s.debugInfo = append(s.debugInfo, s.p.Sprintf("Cameras count %d", s.cameraComponent.Len())) - s.debugInfo = append(s.debugInfo, s.p.Sprintf("Destroys count %d", s.destroyComponentType.Len())) - s.debugInfo = append(s.debugInfo, s.p.Sprintf("Pprof %d", pprofEnabled)) - - ebitenutil.DebugPrint(mainCamera.debugLayer.image, strings.Join(s.debugInfo, "\n")) - s.debugInfo = s.debugInfo[:0] -} -func (s *cameraSystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs-370k/color-system.go b/examples/ebiten-ecs-370k/color-system.go deleted file mode 100644 index e350e361..00000000 --- a/examples/ebiten-ecs-370k/color-system.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package main - -import ( - ecs2 "gomp/pkg/ecs" - "image/color" -) - -type colorSystem struct { - transformComponent *ecs2.ComponentManager[transform] - healthComponent *ecs2.ComponentManager[health] - colorComponent *ecs2.ComponentManager[color.RGBA] - movementComponent *ecs2.ComponentManager[movement] - - baseColor color.RGBA -} - -func (s *colorSystem) Init(world *ecs2.EntityManager) { - s.transformComponent = transformComponentType.GetManager(world) - s.healthComponent = healthComponentType.GetManager(world) - s.colorComponent = colorComponentType.GetManager(world) - s.movementComponent = movementComponentType.GetManager(world) - - s.baseColor = color.RGBA{25, 220, 200, 255} -} -func (s *colorSystem) Run(world *ecs2.EntityManager) { - s.colorComponent.AllParallel(func(ei ecs2.Entity, c *color.RGBA) bool { - health := s.healthComponent.Get(ei) - if health == nil { - return true - } - - hpPercentage := float32(health.hp) / float32(health.maxHp) - - c.R = uint8(hpPercentage * float32(s.baseColor.R)) - c.G = uint8(hpPercentage * float32(s.baseColor.G)) - c.B = uint8(hpPercentage * float32(s.baseColor.B)) - c.A = s.baseColor.A - - return true - }) -} -func (s *colorSystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs-370k/components.go b/examples/ebiten-ecs-370k/components.go deleted file mode 100644 index 84d35674..00000000 --- a/examples/ebiten-ecs-370k/components.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ -package main - -import ( - ecs2 "gomp/pkg/ecs" - "image/color" - - "github.com/hajimehoshi/ebiten/v2" -) - -type transform struct { - x, y int32 -} - -type health struct { - hp, maxHp int32 -} - -type movement struct { - goToX, goToY int32 -} - -type cameraLayer struct { - image *ebiten.Image - zoom float64 -} -type camera struct { - mainLayer cameraLayer - debugLayer cameraLayer -} - -type empty struct{} - -const ( - transformId ecs2.ComponentID = iota - healthId - colorId - movementId - cameraId - destroyId -) - -var transformComponentType = ecs2.CreateComponentService[transform](transformId) -var healthComponentType = ecs2.CreateComponentService[health](healthId) -var colorComponentType = ecs2.CreateComponentService[color.RGBA](colorId) -var movementComponentType = ecs2.CreateComponentService[movement](movementId) -var cameraComponentType = ecs2.CreateComponentService[camera](cameraId) -var destroyComponentType = ecs2.CreateComponentService[empty](destroyId) - -// spawn creature every tick with random hp and position -// each creature looses hp every tick -// each creature has color that depends on its own maxHP and current hp -// when hp == 0 creature dies - -// spawn system -// movement system -// hp system -// destroy system diff --git a/examples/ebiten-ecs-370k/destroy-system.go b/examples/ebiten-ecs-370k/destroy-system.go deleted file mode 100644 index b84cb587..00000000 --- a/examples/ebiten-ecs-370k/destroy-system.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package main - -import ( - ecs2 "gomp/pkg/ecs" - "image/color" -) - -type destroySystem struct { - transformComponent *ecs2.ComponentManager[transform] - healthComponent *ecs2.ComponentManager[health] - colorComponent *ecs2.ComponentManager[color.RGBA] - movementComponent *ecs2.ComponentManager[movement] - destroyComponent *ecs2.ComponentManager[empty] - - n int -} - -func (s *destroySystem) Init(world *ecs2.EntityManager) { - s.transformComponent = transformComponentType.GetManager(world) - s.healthComponent = healthComponentType.GetManager(world) - s.colorComponent = colorComponentType.GetManager(world) - s.movementComponent = movementComponentType.GetManager(world) - s.destroyComponent = destroyComponentType.GetManager(world) - -} -func (s *destroySystem) Run(world *ecs2.EntityManager) { - s.n = 0 - s.destroyComponent.All(func(e ecs2.Entity, h *empty) bool { - world.Delete(e) - entityCount-- - - return true - }) -} -func (s *destroySystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs-370k/game.go b/examples/ebiten-ecs-370k/game.go deleted file mode 100644 index 0c782cac..00000000 --- a/examples/ebiten-ecs-370k/game.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package main - -import ( - "github.com/hajimehoshi/ebiten/v2" - ecs2 "gomp/pkg/ecs" -) - -type game struct { - world *ecs2.EntityManager - cameraComponents *ecs2.ComponentManager[camera] - op *ebiten.DrawImageOptions -} - -func newGame() game { - world := ecs2.NewEntityManager("1 mil pixel") - - world.RegisterComponentServices( - &destroyComponentType, - &cameraComponentType, - &transformComponentType, - &healthComponentType, - &colorComponentType, - &movementComponentType, - ) - - // world.RegisterSystems(). - // Sequential( - // new(spawnSystem), - // new(hpSystem), - // new(colorSystem), - // new(destroySystem), - // new(cameraSystem), - // ) - - newGame := game{ - world: &world, - cameraComponents: cameraComponentType.GetManager(&world), - op: new(ebiten.DrawImageOptions), - } - - return newGame -} - -func (g *game) Update() error { - err := g.world.FixedUpdate() - if err != nil { - return err - } - - return nil -} - -func (g *game) Draw(screen *ebiten.Image) { - var mainCamera *camera - - g.cameraComponents.AllData(func(c *camera) bool { - mainCamera = c - return false - }) - - if mainCamera == nil { - return - } - - g.op.GeoM.Reset() - g.op.GeoM.Scale(mainCamera.mainLayer.zoom, mainCamera.mainLayer.zoom) - screen.DrawImage(mainCamera.mainLayer.image, g.op) - - g.op.GeoM.Reset() - g.op.GeoM.Scale(mainCamera.debugLayer.zoom, mainCamera.debugLayer.zoom) - screen.DrawImage(mainCamera.debugLayer.image, g.op) -} - -func (g *game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { - return outsideWidth, outsideHeight -} diff --git a/examples/ebiten-ecs-370k/hp-system.go b/examples/ebiten-ecs-370k/hp-system.go deleted file mode 100644 index 197baa80..00000000 --- a/examples/ebiten-ecs-370k/hp-system.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package main - -import ( - ecs2 "gomp/pkg/ecs" - "image/color" -) - -type hpSystem struct { - transformComponent *ecs2.ComponentManager[transform] - healthComponent *ecs2.ComponentManager[health] - colorComponent *ecs2.ComponentManager[color.RGBA] - movementComponent *ecs2.ComponentManager[movement] - destroyComponent *ecs2.ComponentManager[empty] -} - -func (s *hpSystem) Init(world *ecs2.EntityManager) { - s.transformComponent = transformComponentType.GetManager(world) - s.healthComponent = healthComponentType.GetManager(world) - s.colorComponent = colorComponentType.GetManager(world) - s.movementComponent = movementComponentType.GetManager(world) - s.destroyComponent = destroyComponentType.GetManager(world) -} -func (s *hpSystem) Run(world *ecs2.EntityManager) { - s.healthComponent.AllParallel(func(entity ecs2.Entity, h *health) bool { - h.hp-- - - if h.hp <= 0 { - s.destroyComponent.Create(entity, struct{}{}) - } - - return true - }) -} -func (s *hpSystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs-370k/main.go b/examples/ebiten-ecs-370k/main.go deleted file mode 100644 index f7a90258..00000000 --- a/examples/ebiten-ecs-370k/main.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package main - -import ( - "flag" - - "github.com/hajimehoshi/ebiten/v2" -) - -const ( - width = 1000 - height = 1000 -) - -var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") - -func main() { - flag.Parse() - - newGame := newGame() - - ebiten.SetRunnableOnUnfocused(true) - ebiten.SetWindowSize(width, height) - ebiten.SetWindowTitle("370k pixel ecs") - - if err := ebiten.RunGame(&newGame); err != nil { - panic(err) - } -} diff --git a/examples/ebiten-ecs-370k/spawn-system.go b/examples/ebiten-ecs-370k/spawn-system.go deleted file mode 100644 index debcb7c2..00000000 --- a/examples/ebiten-ecs-370k/spawn-system.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package main - -import ( - "fmt" - ecs2 "gomp/pkg/ecs" - "image/color" - "log" - "math/rand" - "os" - "runtime/pprof" - - "github.com/hajimehoshi/ebiten/v2" - "github.com/hajimehoshi/ebiten/v2/inpututil" -) - -type spawnSystem struct { - transformComponent *ecs2.ComponentManager[transform] - healthComponent *ecs2.ComponentManager[health] - colorComponent *ecs2.ComponentManager[color.RGBA] - movementComponent *ecs2.ComponentManager[movement] -} - -const ( - minHpPercentage = 20 - minMaxHp = 500 - maxMaxHp = 2000 -) - -var entityCount = 0 -var pprofEnabled = false - -func (s *spawnSystem) Init(world *ecs2.EntityManager) { - s.transformComponent = transformComponentType.GetManager(world) - s.healthComponent = healthComponentType.GetManager(world) - s.colorComponent = colorComponentType.GetManager(world) - s.movementComponent = movementComponentType.GetManager(world) -} -func (s *spawnSystem) Run(world *ecs2.EntityManager) { - if ebiten.IsKeyPressed(ebiten.KeySpace) { - for range rand.Intn(1000) { - - newCreature := world.Create("Creature") - - t := transform{ - x: rand.Int31n(1000), - y: rand.Int31n(1000), - } - - s.transformComponent.Create(newCreature, t) - - maxHp := minMaxHp + rand.Int31n(maxMaxHp-minMaxHp) - hp := int32(float32(maxHp) * float32(minHpPercentage+rand.Int31n(100-minHpPercentage)) / 100) - - h := health{ - hp: hp, - maxHp: maxHp, - } - - s.healthComponent.Create(newCreature, h) - - c := color.RGBA{ - R: 0, - G: 0, - B: 0, - A: 0, - } - - s.colorComponent.Create(newCreature, c) - - m := movement{ - goToX: t.x, - goToY: t.y, - } - - s.movementComponent.Create(newCreature, m) - - entityCount++ - } - } - - if inpututil.IsKeyJustPressed(ebiten.KeyF9) { - if *cpuprofile != "" { - if pprofEnabled { - pprof.StopCPUProfile() - fmt.Println("CPU Profile Stopped") - } else { - f, err := os.Create(*cpuprofile) - if err != nil { - log.Fatal(err) - } - pprof.StartCPUProfile(f) - fmt.Println("CPU Profile Started") - } - - pprofEnabled = !pprofEnabled - } - } -} -func (s *spawnSystem) Destroy(world *ecs2.EntityManager) {} diff --git a/examples/ebiten-ecs/client.go b/examples/ebiten-ecs/client.go deleted file mode 100644 index 44aa51c8..00000000 --- a/examples/ebiten-ecs/client.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "gomp/examples/ebiten-ecs/scenes" - systems2 "gomp/examples/ebiten-ecs/systems" - "gomp/pkg/gomp" - - "time" - - "github.com/hajimehoshi/ebiten/v2" -) - -const tickRate = time.Second / 60 - -func main() { - game := gomp.NewGame(tickRate) - game.Debug = false - - game.LoadScene(scenes.VillageScene) - - // TODO: move BodySystem inside gomp engine such as render system - game.RegisterSystems( - gomp.BodySystem, - systems2.IntentSystem, - systems2.HeroMoveSystem, - ) - - e := game.Ebiten() - - e.RegisterInputHandlers() - - ebiten.SetRunnableOnUnfocused(true) - ebiten.SetWindowSize(1280, 720) - ebiten.SetWindowTitle("Engine") - - err := ebiten.RunGame(e) - if err != nil { - panic(err) - } -} diff --git a/examples/ebiten-ecs/components/hero-intent-component.go b/examples/ebiten-ecs/components/hero-intent-component.go deleted file mode 100644 index 0bde2200..00000000 --- a/examples/ebiten-ecs/components/hero-intent-component.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package components - -import ( - "gomp/pkg/gomp" - - "github.com/yohamta/donburi/features/math" -) - -type HeroIntentData struct { - Move math.Vec2 - Jump bool - Fire bool -} - -var HeroIntentComponent = gomp.CreateComponent[HeroIntentData]() diff --git a/examples/ebiten-ecs/entities/Hero.go b/examples/ebiten-ecs/entities/Hero.go deleted file mode 100644 index 350ed9b8..00000000 --- a/examples/ebiten-ecs/entities/Hero.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package entities - -import ( - "gomp/examples/ebiten-ecs/components" - "gomp/examples/ebiten-ecs/resources" - "gomp/pkg/gomp" - - "github.com/jakecoffman/cp/v2" -) - -type HealthData struct { - Health int - MaxHealth int -} - -var HealthComponent = gomp.CreateComponent[HealthData]() -var ManaComponent = gomp.CreateComponent[uint16]() - -var Hero = gomp.CreateEntity( - gomp.SpriteComponent.New(resources.Sprites("big_demon_idle_anim_f0.png")), - gomp.BodyComponent.New(*cp.NewKinematicBody()), - - components.HeroIntentComponent.New(components.HeroIntentData{}), - HealthComponent.New(HealthData{Health: 100, MaxHealth: 100}), - ManaComponent.New(125), -) diff --git a/examples/ebiten-ecs/resources/resources.go b/examples/ebiten-ecs/resources/resources.go deleted file mode 100644 index ab4fe486..00000000 --- a/examples/ebiten-ecs/resources/resources.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -This Source Code Form is subject to the terms of the Mozilla -Public License, v. 2.0. If a copy of the MPL was not distributed -with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -*/ - -package resources - -import ( - "embed" - "gomp/pkg/gomp" -) - -//go:embed **/*.png -var fs embed.FS - -var Sprites = gomp.CreateSpriteResource(fs, "sprites") diff --git a/examples/ebiten-ecs/resources/sprites/area.png b/examples/ebiten-ecs/resources/sprites/area.png deleted file mode 100644 index 98bb19c309dc0247454f9a5a996f614e40d94338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}NuDl_ArY;~ zAwdQ|&l@x}WGd*o7aq9w!mMrW#ax~*kCH7C7BDDRNj2=eRGldr`-54INymU~Z!I4) zkhM57!GyPGS>r_$gIR2Dmk(q}N%%5D1sQt;7MM$X5MW?<{Lf^`=^J+GK-(BRUHx3v IIVCg!04zKZuF3^Y+Be!v4Z+6r1KJ`gDn$ZM$XHF-d!`@mIcNrkcxKuR55aUw-z&TzKZY><+I zkshbaF~E);J2rrGZv3;LT<|r7?tT+oA{0W%taC*jX)v5Pw!%#bNCA&k0RRA} z{%36bFxiK5cSBYpwJHM~frpn@008)Sewu7w)-zu%BWw*g=SIBnS({h0F$Sw2OMo)Q zi5fpltETW}JzFu}H-4CrkTR)mfpczb^J>~(1n#p0`psi^{g&GbkpP!|N(76K1NB1! zQXSljvnatiTSHP2;ytDsSF&cL=}!Xv8(eyG4lVU44k2Vx`j-E?WXDcL^O2dN zc$OnMNsP9{|0O^RD7E3B-$?}-Jqfk<{rlTZ@oCSwnou*K1?Ft&xfEO_umn{@U)FG} z3Ux$3EjWNtj1K3HRl*pS%?Srn2HL!WQ(ve8#KEE$Q36zUIa#(qtEZ?OTRR)t6H+_Y z53q8a^4~ABakU2sA!MsMBHG(yAyP=)5a+jv7*P`1q*gzQRa#)3)atj2fYyYj_#wUl X|9mJ#|AKhb00000NkvXXu0mjfdYa9a diff --git a/examples/ebiten-ecs/resources/sprites/big_demon_idle_anim_f1.png b/examples/ebiten-ecs/resources/sprites/big_demon_idle_anim_f1.png deleted file mode 100644 index 718ec7ad9db372340ce6005d4dcce5d5d7cb3a7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 497 zcmVK)VGS1Ggv6yHru$aSVb{Y|{SqmfWcK;?0YnK9}}8UbL|uh|h9 z0lLqn-`D4dYqWf+yTD@3Yc0v1=w0bq`lw`6F%WYnui Z@ePS{DbI_yPQ(BJ002ovPDHLkV1h(w+_?Y% diff --git a/examples/ebiten-ecs/resources/sprites/big_demon_idle_anim_f3.png b/examples/ebiten-ecs/resources/sprites/big_demon_idle_anim_f3.png deleted file mode 100644 index d059bc42b29017a95e65d9167fea00b93c0514e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 468 zcmV;_0W1EAP)P2fxtzI`GADjek$?_6e zUM}XL&Vj%-d?W<`pdKxnB1PM!sH|PsRCW;U3C0+)HCGE{XpEU2NijQhLH#d*YXT=C z5fCH5!zVfCPPQ=HSlwn-1Ds_&TC`m{fB%8a8lYT;+|~z8nm4=^Y7L1Xd{^$C?-s_~E|!*2^gV2o(|cmfHFjfnBd*_j!qPZJl4pz1$)N z+ReGLJ(2=hUZN?|&5`VUXNyYcI@QF;2d*8O8mcuw1YGRZgvt&eiRUC1HNeBf9}>-Z ztU5|*xM>Dj1T?r0kV}>X$(sVV2F^k~TCp9yIv^2|(i37U5%>jZX8JB^^4c~40000< KMNUMnLSTXo{l)qK diff --git a/examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f0.png b/examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f0.png deleted file mode 100644 index f42aeecf60befbf5b4c6bf81c455539b4c9ee269..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 466 zcmV;@0WJQCP)di6C3ASNQmd;oO>QN&3L{DJZBDc%|}0sTu!hAQu0$Gn0zVFc^ZLt){784WlNB5 zMDiBU-J{gFlbjL17bCtA^z-xmWg4?@0YwO*;^d>5FWFi_GMCJfj%TTctO8WN(so-^ z3FC#5liT&IJ}=4vO}aQR+T3%AgsK37_5xqGaB8;Vi1V}!&3?e(A%qG`arfwxeL#by zHJf??MNS0-7z$E$C8VHDC)JcJkaB?S1-OVRkP4W3BTA9YB2aSgV{(7Cd<3ilxTte5 z%LDCQfX-W_6w_psoW{yoO0oJrK%wm@=XD%m{$nD^zTy@90$v#(p5Z6qN&o-=07*qo IM6N<$g7Hkuq5uE@ diff --git a/examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f1.png b/examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f1.png deleted file mode 100644 index d8f3c9c6479d9f98888173f9ed2ef61811e59fd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 503 zcmV+vwG>}qhNX~+_SVrR^>uFFUkUBk23&&EQ^fujsB>~GzS0bGwAwJ307~qI8 t7M;WFK}**-g=`U8oohC>?tkNd;0N&SAq2BR_e%f(002ovPDHLkV1iau-HreN diff --git a/examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f2.png b/examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f2.png deleted file mode 100644 index aa07f45a2da06c2a83becef6112a969ed47e8da9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 518 zcmV+h0{Q)kP)b>k6o!9Sm3)I70LepTubFZIPd!3z;>iceHFAMWl`_#O5(lu40VTIYo|}IC$ImbUgQJ*v9nIq>j}M{ z9FS1CYbFP1k!T#C&+Yq5R4#HtAijkDUdQU(7OWD8tR#VmU4fj*N+bqY3nUvrq8vyO zfInf(0Bx5-hyX%P7=v2NSE3-X6Cyo1#VbJD>HoP$Hh33s$;8quh>A@}Y<5#Jd2vSM zLo89zZ73NC0GH1Xww=3>W&n~K%r|W7vG$Z^0QSv&2zuu??H0#wlPqmNI%i0%4-9qZ zWWhPI8FC6hPzoDU?O z&^J!EBPKeAGY&A{1-?N^a2ge6;D0Wq)cx@G&IUAKrIaj3KbrR_b5}@-SO5S307*qo IM6N<$g2^7=7ytkO diff --git a/examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f3.png b/examples/ebiten-ecs/resources/sprites/big_demon_run_anim_f3.png deleted file mode 100644 index 20a1f222dabd9a6799db102e065bfa416d91b06b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 486 zcmV@P)TBaz;PU!;z znT9ByNb2MO?AWnm$3`fn9$WlB-W|A<&?*94khPgOyo&(EG)>v-e&z(@7QC1v003Oa z=h$lJ?Lh(S!y&5#(vDBJGn{VcnO%YffiM{EWKE58eAo<7$MCt7NNRqx2ezK?T>nJ| zxaR3A1AS%ua}KDFM20N^lE4L8?V#1pcRq!#8_;z#v#C#tMDiGhL0j#h9}RTY&fa&{ z`R8X)=jX$G>I{o?hfHb8@vH;9&OZk_kCNlc%Zde(pe5LX&bp{NiajvgtzQ6lQ8H4$ zRlsx4{r-M`vc}YL6oqJ-rb8@swDTo9bti~DfD3F9r4#|N2SPF-#_zA>`{EMh)GZ8* zcI4fSJs`j6xD2I8tG`C?qjj{l9>c ckh(mDe+!Nd-%Z}J#sB~S07*qoM6N<$f;*Vo9RL6T diff --git a/examples/ebiten-ecs/resources/sprites/big_zombie_idle_anim_f0.png b/examples/ebiten-ecs/resources/sprites/big_zombie_idle_anim_f0.png deleted file mode 100644 index c9f6d2fea2c1490242ea89619742b1928dc33790..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 475 zcmV<10VMv3P)W|3c@`K;}YA=lq2d@R}k01KxzkLJe6uwBRAD zJ*7jCksU{OlKZjvEsjp|y_dUpGSH((kAKFQ>x)4U>`(1&w_A4J3s>^Xhp<||Ei%kD zBdn!&2rRJYim&LO1pDb&9F=uC7Ds3G z7}W-r07c#g5rxJWC4g*!POc}GX@;klR{$WNCXi#O1SoQ4ur^U`m@ikxQ8Wr@k^_f- z9w)N3_f5o!{F!?|rdcIIL?PltvSyqE>w7?^8Cib)f{J1OFtG&aL{!h4XF5*-0P@NO zc#d}#z!7nOd*vj6oC2q;4RrE=@B5v|ce|Z&pA(Uu=*hpvIrU+5ef=p1P1oL>lmtQnFY$R<1%YRJ-|1rJ{B zDV<~#$LKxTq+W81?I-))`E+`~6<1u*F>cy?EQB~#Ylp+ZtLJSS@^B^*&LjX}vK=Cv z$y!k^G;+X@KdQW}eLQ?)vK^N9Tqhbk000{uV50*7D4p;{;|v%Vs`z|MymHWa2s?+j zTHlmYj@c)F`lzp%CIY9w~NX z`#aY<%K;a8UEQux+dvAi$yFrJ8mj1g7T7gP0DB9nL^@6|U+w^ayt?2#BIfUHauYDy z6&I$9mGdaFGN9w6BpQ#@iLI`qxZdl_%mLG!10lq*wF9=zDw4If&zC#AKHs0!shPkE zp^g)B>eZ2RK!&hU*9ux~LBHQ`MV@7ubDb1GHE0Vj2L?e8X~VH9R7I+&h2A`1(|Ol(kbHdURv zaJ@F)-KBw=Jd@LP7Y}Ddr>#F*SjA~*OTVn*NM&!0KiH{Sjh+g8Ye>0IRn}S8=I#xC=h)y-n~dUW{YL>{QUR;00%V}yr+OMw$C%+-Prt& zTK95*B0p6(Yp!E(3NXn<%8wd~^mr7_noEH95!ghsDDm?40RYY`1#(36?oDzXFw+$Z zv&E7eMXwC#aT6&=o{0lnsZs2049#8Y9QZW%f!7h(B5cT_#OJ2MZNiS;>*WYENt+0f za%UFW;B=rO1IbE4#t~FrSofuwB4c?8(2#M2^>){fA8xJy;L99^Qx`(l z^mERzGa!WMl9fD4YqF9@d(AoO8CU`|`50szS?4GLUczk*R0P<@CS%zAG zCf5cV6E%kQc4sX`tAHUnaJYGzsnNF|BF)s#^Z}I@y$BgcNHfK%aSojOfXWLp{bqwo zVQy#;VY++AOK2_UP$E9wA~JP?NAB=Wkht?Q%!D!^1Y zWndXBP&F-O$_>6U;Q#;t07*qoM6N<$f&jz0L;wH) diff --git a/examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f0.png b/examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f0.png deleted file mode 100644 index ed1ac2cb0df32de480cee3b389bf12a8419034ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 447 zcmV;w0YLtVP)Fx#qeyveBcOi7O zpYsme0U<=6CURnHn#jpnbB#&^OMoVygN!5V8YO^Cfk|!#Rz->Dmv;ale{CS!Pz%uH zy1?c{&0(|KTU*g8U`!4iZl2|8Huqy>xjN1qP(?Y2ka2`8SF9fAztKbt?XV73Y=Y0PZ|gq02sNn-IR|V3b=#qCyW#(_;4}#-7ffMXr`SaphT-Jq=H3`o p?pXqK|N71Y&ZqgJk5Vp)4L_sJqvpKg#^?Y5002ovPDHLkV1iAN#_9k7 diff --git a/examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f1.png b/examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f1.png deleted file mode 100644 index b8916f133a9f85b67c0f639fcef49045e420b0f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 499 zcmV6}*x0k0j>7w{%L7HY`Sp#=|K z?a3Wd8O3pRCppk1|B^_S?*Dw!9R~hcXTBSS5Vh_3cDrT8_5u(>)R&K8Gk$-_FyD;v zs)o&&3*ii)&8M>5n}50f#C$W}8*>0ITmgXJH#9OZl_ho=DV@v#&kVS%r$D?iFq=ro zD1=Hg>$*5mWGpWLEg6MaEZ5EW>Fx#q_U0&^yBNBrpR> z0}DW#cS%N}b&dickH9oH1FJN{^Xd%%$X}btGSmRtTwky*s4gv*YilW50ovxk?#<&w zb)J1&aiWgX2UMCh5Hbo8CyG_$969#^m1bo6%?1?%wJi%Q0Mm#%^Y)tFRRDl&xd89R zdjoJF?r*QW0LUuvirP}II}ijxFY}@(tj|dRDnMU3WneTMATKM7h6A?2$Je*@Ir|c7 zQx|iN*Z_H1Aup@uKX*?M1lG&!)Csv+-~w6!gb;PR0?fd+%jr@%y}k+ms_!oNqW}~| p!T#Romai4Sw+C<`PdW(zegFgR!r~^}m|*|_002ovPDHLkV1m}r+Ts8J diff --git a/examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f2.png b/examples/ebiten-ecs/resources/sprites/big_zombie_run_anim_f2.png deleted file mode 100644 index b861473b7272f9667b66984bc9a30904cba80a4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 515 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%0AqrSyO;0$^S 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