refactoring.

This commit is contained in:
Andrey Parhomenko 2024-05-28 13:24:12 +05:00
parent e697383867
commit 234f4f4d0c
48 changed files with 1159 additions and 974 deletions

View file

@ -1,99 +0,0 @@
package gg
import (
"github.com/hajimehoshi/ebiten/v2"
//"fmt"
)
type Animation []*Image
type AnimationDefine struct{
Id AnimationId
Indexes []ImageRectIndex
}
// Animation define
func AD(id AnimationId, indexes ...ImageRectIndex) AnimationDefine {
return AnimationDefine{
Id: id,
Indexes: indexes,
}
}
func (ad AnimationDefine) DefRow(y int, xs ...int) AnimationDefine {
for _, x := range xs {
ad.Indexes = append(ad.Indexes, ImageRectIndex{x, y})
}
return ad
}
func (ad AnimationDefine) DefCol(x int, ys ...int) AnimationDefine {
for _, y := range ys {
ad.Indexes = append(ad.Indexes, ImageRectIndex{x, y})
}
return ad
}
type AnimationId int
type AnimationSet map[AnimationId] Animation
func AnimationSetFromImage(
img *Image,
w, h, gx, gy int,
defines ...AnimationDefine,
) (AnimationSet, error) {
set := AnimationSet{}
r := img.Bounds()
fw, fh := r.Dx()/w, r.Dy()/h
for _, define := range defines {
animation := make(Animation, len(define.Indexes))
for i := range animation {
idx := define.Indexes[i]
animation[i] = ebiten.NewImageFromImage(img.SubImage(
ImageRect{
Min: ImagePoint{idx.X*fw+gx, idx.Y*fh+gy},
Max: ImagePoint{(idx.X+1)*fw-gx, (idx.Y+1)*fh-gy},
},
))
}
set[define.Id] = animation
}
return set, nil
}
// The type implements animated sprites.
type AnimatedSprite struct {
Sprite
Animations AnimationSet
AnimationId AnimationId
CurrentFrame int
// This is time between animation frames, not
// engine ones.
TimeBetweenFrames Duration
duration Duration
}
func (as *AnimatedSprite) Animate(id AnimationId) bool {
if as.AnimationId == id {
return true
}
_, ok := as.Animations[id]
if ok {
as.duration = 0
as.AnimationId = id
}
return ok
}
func (as *AnimatedSprite) Draw(c *Context) []EVertex {
as.duration += c.DrawDt()
frames := as.Animations[as.AnimationId]
fullTime := Duration(len(frames)) * as.TimeBetweenFrames
if as.duration > fullTime {
as.duration -= fullTime
}
as.Images[0] = frames[(as.duration/as.TimeBetweenFrames)%Duration(len(frames))]
return as.Sprite.Draw(c)
}

51
ax/ani.go Normal file
View file

@ -0,0 +1,51 @@
package ax
import (
"surdeus.su/core/gg"
)
// Unique identifier for animation
// in the animation set.
type AnimationId int
// The type describes set of animations
// to switch between.
type AnimationSet map[AnimationId] Animation
// Make new animation set from an image.
func AnimationSetFromImage(
img *gg.Image,
// Width and height of one frame respectively.
w, h int,
// Gaps for X and Y respectively.
gx, gy int,
defines ...AnimationDefine,
) (AnimationSet, error) {
set := AnimationSet{}
r := img.Bounds()
fw, fh := r.Dx()/w, r.Dy()/h
for _, define := range defines {
animation := make(Animation, len(define.Indexes))
for i := range animation {
idx := define.Indexes[i]
rect := gg.ImageRect{
Min: gg.ImagePoint{
idx.X*fw+gx,
idx.Y*fh+gy,
},
Max: gg.ImagePoint{
(idx.X+1)*fw-gx,
(idx.Y+1)*fh-gy,
},
}
subImg := img.SubImage(rect)
animation[i] = gg.NewImageFromImage(subImg)
}
set[define.Id] = animation
}
return set, nil
}

52
ax/define.go Normal file
View file

@ -0,0 +1,52 @@
package ax
import "surdeus.su/core/gg"
type Animation []*gg.Image
type RectIndex struct {
X, Y int
}
// The type is used
// to structurely define
// animations.
type AnimationDefine struct{
Id AnimationId
Indexes []RectIndex
}
// Animation define shortcut.
func AD(
id AnimationId,
indexes ...RectIndex,
) AnimationDefine {
return AnimationDefine{
Id: id,
Indexes: indexes,
}
}
func (ad AnimationDefine) DefRow(
y int, xs ...int,
) AnimationDefine {
for _, x := range xs {
ad.Indexes = append(
ad.Indexes,
RectIndex{x, y},
)
}
return ad
}
func (ad AnimationDefine) DefCol(
x int, ys ...int,
) AnimationDefine {
for _, y := range ys {
ad.Indexes = append(
ad.Indexes,
RectIndex{x, y},
)
}
return ad
}

View file

@ -1,59 +1,19 @@
package gg package gg
// Implements the camera component import "surdeus.su/core/gg/mx"
// for the main window.
type Camera struct { // The type describes what
// The shaders that will be applied to everything // a camera object must implement.
// that the camera shows. type Camera interface {
ShaderOptions // Get the matrice to apply
Transform // camera's features.
buffered bool GetRealMatrice(Context) mx.Matrice
buf Matrix
engine *Engine // The way to convert from real to absolute.
GetAbsMatrice(Context) mx.Matrice
// The shaders to apply on
// everything on the camera.
GetShaderOptions() *ShaderOptions
} }
func (e *Engine) NewCamera() *Camera {
ret := &Camera{}
ret.Transform = T()
ret.engine = e
return ret
}
// Returns the matrix satysfying camera
// position, scale and rotation to apply
// it to the objects to get the real
// transform to display on the screen.
func (c *Camera)RealMatrix() Matrix {
// Bufferization
if c.buffered {
return c.buf
}
g := Matrix{}
position := c.Position().Neg()
around := c.Around()
scale := c.Scale()
rotation := c.Rotation()
g.Translate(-position.X, -position.Y)
g.Rotate(rotation)
size := c.engine.AbsWinSize()
g.Translate(around.X * size.X, around.Y * size.Y)
g.Scale(scale.X, scale.Y)
c.buf = g
c.buffered = true
return g
}
// The matrix to convert things into the
// inside engine representation,
// get the position of cursor inside the world
// basing on its window position.
func (c *Camera) AbsMatrix() Matrix {
m := c.RealMatrix()
m.Invert()
return m
}

View file

@ -2,6 +2,7 @@ package main
import ( import (
"surdeus.su/core/gg" "surdeus.su/core/gg"
"surdeus.su/core/gg/ox"
"github.com/hajimehoshi/ebiten/v2/examples/resources/images" "github.com/hajimehoshi/ebiten/v2/examples/resources/images"
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts" "github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
//_ "github.com/silbinarywolf/preferdiscretegpu" //_ "github.com/silbinarywolf/preferdiscretegpu"
@ -13,11 +14,11 @@ import (
type Context = gg.Context type Context = gg.Context
type Grid struct { type Grid struct {
gg.Sprite ox.Sprite
Cage, Width gg.Float Cage, Width gg.Float
} }
func (grid *Grid) Update(c *Context) { func (grid *Grid) OnUpdate(c *Context) {
//d := c.IsPressed(gg.KeyShift) //d := c.IsPressed(gg.KeyShift)
for _, ev := range c.Events { for _, ev := range c.Events {
switch e := ev.(type) { switch e := ev.(type) {
@ -34,7 +35,7 @@ func (grid *Grid) Update(c *Context) {
} }
} }
func (grid *Grid) Draw(c *Context) []gg.EVertex { func (grid *Grid) OnDraw(c *Context) []gg.EVertex {
scale := c.Camera.Scale() scale := c.Camera.Scale()
pos := c.Camera.Position() pos := c.Camera.Position()
grid.Uniforms["Zoom"] = gg.Float(scale.X) grid.Uniforms["Zoom"] = gg.Float(scale.X)

View file

@ -1,109 +1,2 @@
package gg package gg
// Implementing the interface lets
// the engine to work faster about
// collisions because it first checks
// if the the bigger collider that
// contains more complicated (accurate) structure
// do collide.
//type ColliderSimplifier interface {
//ColliderSimplify() Triangle
//}
type CollisionMap map[CollisionType] []Collision
type CollisionType int
const (
CollisionNo CollisionType = iota
CollisionStaticPhysics
CollisionTrigger
CollisionLast
)
// The structure contains collision information.
type Collision struct {
Type CollisionType
What, With Collider
// Points of Self contained in
Points Points
Crosses []LineCross
}
// Implementing the interface lets the engine
// to determine if the object collides with anything.
type Collider interface {
// Checks whether the object is collidable
CollisionType() CollisionType
IsCollidable() bool
Verticer
Edger
PointContainer
}
type Collidability struct {
Type CollisionType
Collidable bool
}
func (c Collidability) CollisionType() CollisionType {
return c.Type
}
func (c Collidability) IsCollidable() bool {
return c.Collidable
}
type PointContainer interface {
ContainedPoints(Points) Points
}
type Verticer interface {
Vertices() Vertices
}
type Edger interface {
Edges() Edges
}
// Implementing the type provides way
// to resolve collisions and other problems.
// The Resolve() is called right after the Update() of Updater.
type Resolver interface {
IsResolvable() bool
Resolve(c *Context)
GetCollisionInterest() []CollisionType
Collider
}
type Resolvability struct {
Resolvable bool
CollisionInterest []CollisionType
}
func (r Resolvability) IsResolvable() bool {
return r.Resolvable
}
func (r Resolvability) GetCollisionInterest() []CollisionType {
return r.CollisionInterest
}
func Collide(c1, c2 Collider) (Collision, bool) {
vertices := c1.Vertices()
es1, es2 := c1.Edges(), c2.Edges()
crosses, doCross := es1.CrossWithEdges(es2)
pts := c2.ContainedPoints(vertices)
return Collision{
Type: c2.CollisionType(),
What: c1,
With: c2,
Points: pts,
Crosses: crosses,
}, len(pts) > 0 || doCross
}
func GetCollisions(c Collider, cs []Collider) []Collision {
ret := []Collision{}
for _, ic := range cs {
if ic == c {
continue
}
col, has := Collide(c, ic)
if has {
ret = append(ret, col)
}
}
return ret
}

View file

@ -1,19 +1,8 @@
package gg package gg
type contextType int
const (
startContext contextType = iota
updateContext
resolveContext
eventContext
drawContext
deleteContext
)
type Context struct { type Context struct {
typ contextType
Events []any Events []any
Collisions []Collision
*Engine *Engine
*Image *Image
} }

109
cx/collider.go Normal file
View file

@ -0,0 +1,109 @@
package cx
// Implementing the interface lets
// the engine to work faster about
// collisions because it first checks
// if the the bigger collider that
// contains more complicated (accurate) structure
// do collide.
//type ColliderSimplifier interface {
//ColliderSimplify() Triangle
//}
type CollisionMap map[CollisionType] []Collision
type CollisionType int
const (
CollisionNo CollisionType = iota
CollisionStaticPhysics
CollisionTrigger
CollisionLast
)
// The structure contains collision information.
type Collision struct {
Type CollisionType
What, With Collider
// Points of Self contained in
Points Points
Crosses []LineCross
}
// Implementing the interface lets the engine
// to determine if the object collides with anything.
type Collider interface {
// Checks whether the object is collidable
CollisionType() CollisionType
IsCollidable() bool
Verticer
Edger
PointContainer
}
type Collidability struct {
Type CollisionType
Collidable bool
}
func (c Collidability) CollisionType() CollisionType {
return c.Type
}
func (c Collidability) IsCollidable() bool {
return c.Collidable
}
type PointContainer interface {
ContainedPoints(Points) Points
}
type Verticer interface {
Vertices() Vertices
}
type Edger interface {
Edges() Edges
}
// Implementing the type provides way
// to resolve collisions and other problems.
// The Resolve() is called right after the Update() of Updater.
type Resolver interface {
IsResolvable() bool
Resolve(c *Context)
GetCollisionInterest() []CollisionType
Collider
}
type Resolvability struct {
Resolvable bool
CollisionInterest []CollisionType
}
func (r Resolvability) IsResolvable() bool {
return r.Resolvable
}
func (r Resolvability) GetCollisionInterest() []CollisionType {
return r.CollisionInterest
}
func Collide(c1, c2 Collider) (Collision, bool) {
vertices := c1.Vertices()
es1, es2 := c1.Edges(), c2.Edges()
crosses, doCross := es1.CrossWithEdges(es2)
pts := c2.ContainedPoints(vertices)
return Collision{
Type: c2.CollisionType(),
What: c1,
With: c2,
Points: pts,
Crosses: crosses,
}, len(pts) > 0 || doCross
}
func GetCollisions(c Collider, cs []Collider) []Collision {
ret := []Collision{}
for _, ic := range cs {
if ic == c {
continue
}
col, has := Collide(c, ic)
if has {
ret = append(ret, col)
}
}
return ret
}

16
draw.go
View file

@ -1,5 +1,15 @@
package gg package gg
// Should implement type Drawing struct {
// some general structure EVertices []EVertice
// for the returned values for the Draw call. }
// The interface describes anything that can be
// drawn. It will be drew corresponding to
// the layers order so the layer must be returned.
type Drawer interface {
Draw(Context) Drawing
GetLayer() Layer
IsVisible() bool
}

194
engine.go
View file

@ -3,13 +3,13 @@ package gg
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/inpututil"
"vultras.su/core/gods/maps" "surdeus.su/core/gods/maps"
//"fmt"
"time" "time"
"slices" "slices"
"sync"
) )
import "surdeus.su/core/gg/mx"
const ( const (
MaxVertices = 1 << 16 MaxVertices = 1 << 16
) )
@ -45,12 +45,13 @@ type WindowConfig struct {
} }
type Objects struct { type Objects struct {
store []Objecter store []Object
} }
// The main structure that represents current state of [game] engine. // The main structure that represents
// current state of [game] engine.
type Engine struct { type Engine struct {
wcfg *WindowConfig wcfg WindowConfig
// The main holder for objects. // The main holder for objects.
// Uses the map structure to quickly // Uses the map structure to quickly
@ -59,7 +60,7 @@ type Engine struct {
// The main camera to display in window. // The main camera to display in window.
// If is set to nil then the engine will panic. // If is set to nil then the engine will panic.
Camera *Camera Camera Camera
drawLastTime time.Time drawLastTime time.Time
drawdt Duration drawdt Duration
@ -71,10 +72,9 @@ type Engine struct {
// Temporary stuff // Temporary stuff
keys, prevKeys []Key keys, prevKeys []Key
buttons MouseButtonMap buttons MouseButtonMap
wheel Vector wheel mx.Vector
cursorPos Vector cursorPos mx.Vector
outerEvents, handleEvents EventChan outerEvents, handleEvents EventChan
wg sync.WaitGroup
//bufs [LayerBufSize]*Image //bufs [LayerBufSize]*Image
vertices map[Layer] []ebiten.Vertex vertices map[Layer] []ebiten.Vertex
@ -114,11 +114,8 @@ func (e *Engine) MouseButtons() []MouseButton {
// Returns new empty Engine. // Returns new empty Engine.
func NewEngine( func NewEngine(
cfg *WindowConfig, cfg WindowConfig,
) *Engine { ) *Engine {
/*w := Float(cfg.Width)
h := Float(cfg.Height)*/
ret := &Engine{} ret := &Engine{}
ret.wcfg = cfg ret.wcfg = cfg
@ -131,55 +128,43 @@ func NewEngine(
} }
// Get the real window size in the current context. // Get the real window size in the current context.
func (c *Engine) RealWinSize() Vector { func (c *Engine) RealWinSize() mx.Vector {
var w, h int var w, h int
if c.wcfg.Fullscreen { if c.wcfg.Fullscreen {
w, h = ebiten.ScreenSizeInFullscreen() w, h = ebiten.ScreenSizeInFullscreen()
} else { } else {
w, h = c.wcfg.Width, c.wcfg.Height w, h = c.wcfg.Width, c.wcfg.Height
} }
return V( return mx.Vector{
Float(w), mx.Float(w),
Float(h), mx.Float(h),
) }
} }
func (c *Engine) AbsWinSize() Vector { func (c *Engine) AbsWinSize() mx.Vector {
return c.RealWinSize().Div(c.Camera.Scale()) return c.RealWinSize().Div(c.Camera.GetScale())
} }
func (e *Engine) EventInput() EventChan { func (e *Engine) EventInput() EventChan {
return e.outerEvents return e.outerEvents
} }
// Add new object considering what // Add new object to the Engine's view.
// interfaces it implements. func (e *Engine) Spawn(o Object) error {
func (e *Engine) Spawn(b Objecter) error {
/*if e.Objects.Has(b) { /*if e.Objects.Has(b) {
return ObjectExistErr return ObjectExistErr
}*/ }*/
b.Start(&Context{Engine: e}) o.OnStart(Context{
obj := b.GetObject() Engine: e,
obj.input = make(chan *Context) })
go func() { e.Objects.store = append(e.Objects.store, o)
for c := range obj.input {
switch c.typ{
case updateContext :
b.Update(c)
case resolveContext :
b.Resolve(c)
}
e.wg.Done()
}
}()
e.Objects.store = append(e.Objects.store, b)
return nil return nil
} }
// Delete object from Engine. // Delete object from Engine.
func (e *Engine) Del(b Objecter) error { func (e *Engine) Delete(b Object) error {
/*if !e.Objects.Has(b) { /*if !e.Objects.Has(b) {
return ObjectNotExistErr return ObjectNotExistErr
} }
@ -216,28 +201,28 @@ func (e *Engine) IsButtoned(b MouseButton) bool {
return ok return ok
} }
func (e *Engine) Wheel() Vector { func (e *Engine) Wheel() mx.Vector {
return e.wheel return e.wheel
} }
func (e *Engine) cursorPosition() Vector { func (e *Engine) cursorPosition() mx.Vector {
x, y := ebiten.CursorPosition() x, y := ebiten.CursorPosition()
return V(Float(x), Float(y)) return mx.Vector{mx.Float(x), mx.Float(y)}
} }
func (e *Engine) CursorPosition() Vector { func (e *Engine) CursorPosition() mx.Vector {
return e.cursorPos return e.cursorPos
} }
func (e *Engine) AbsCursorPosition() Vector { func (e *Engine) AbsCursorPosition() mx.Vector {
return e.CursorPosition().Apply(e.Camera.AbsMatrix()) return e.CursorPosition().Apply(e.Camera.GetAbsMatrice())
} }
func (e *Engine) Runes() []rune { func (e *Engine) Runes() []rune {
return e.runes return e.runes
} }
func (e *engine) UpdateEvents() []any { func (e *engine) updateEvents() []any {
eng := (*Engine)(e) eng := (*Engine)(e)
e.prevKeys = e.keys e.prevKeys = e.keys
@ -261,8 +246,8 @@ func (e *engine) UpdateEvents() []any {
} }
x, y := ebiten.Wheel() x, y := ebiten.Wheel()
e.wheel = V(x, y) e.wheel = mx.Vector{x, y}
if !(e.wheel.Eq(ZV)) { if !(e.wheel.Eq(mx.ZV)) {
events = append(events, &WheelChange{ events = append(events, &WheelChange{
Offset: e.wheel, Offset: e.wheel,
}) })
@ -285,14 +270,14 @@ func (e *engine) UpdateEvents() []any {
realPos := eng.cursorPosition() realPos := eng.cursorPosition()
if !realPos.Eq(e.cursorPos) { if !realPos.Eq(e.cursorPos) {
absM := eng.Camera.AbsMatrix() absM := eng.Camera.GetAbsMatrice()
absPrevPos := e.cursorPos.Apply(absM) absPrevPos := e.cursorPos.Apply(absM)
absPos := realPos.Apply(absM) absPos := realPos.Apply(absM)
events = append(events, &MouseMove{ events = append(events, &MouseMove{
Real: realPos.Sub(e.cursorPos), RealDelta: realPos.Sub(e.cursorPos),
Abs: absPos.Sub(absPrevPos), AbsDelta: absPos.Sub(absPrevPos),
}) })
e.cursorPos = realPos e.cursorPos = realPos
} }
@ -311,78 +296,20 @@ func (e *engine) Update() error {
// Maybe should think of the better way, // Maybe should think of the better way,
// but for it is simple enough. // but for it is simple enough.
events := e.UpdateEvents() events := e.updateEvents()
c := &Context{ c := Context{
Engine: eng, Engine: eng,
typ: updateContext,
Events: events, Events: events,
} }
for _, object := range e.Objects.store { for _, object := range e.Objects.store {
e.wg.Add(1) object.OnUpdate(c)
object.Input() <- c
} }
e.wg.Wait()
e.Resolve()
e.lastTime = time.Now() e.lastTime = time.Now()
e.frame++ e.frame++
return nil return nil
} }
func (e *engine) Resolve() {
eng := (*Engine)(e)
type resHold struct{
Resolver Resolver
input chan *Context
}
colliders := map[CollisionType] []Collider{}
resolvers := []resHold{}
for _, object := range e.Objects.store {
if object.IsResolvable() {
interests := object.GetCollisionInterest()
for _, interest := range interests {
_, ok := colliders[interest]
if !ok {
colliders[interest] = []Collider{}
}
}
resolvers = append(resolvers, resHold{
object,
object.Input(),
})
}
}
for _, object := range e.Objects.store {
if object.IsCollidable() {
typ := object.CollisionType()
_, ok := colliders[typ]
if !ok {
continue
}
colliders[typ] = append(colliders[typ], object)
}
}
for _, resolver := range resolvers {
interests := resolver.Resolver.GetCollisionInterest()
cols := []Collision{}
for _, interest := range interests {
cols = append(cols, GetCollisions(resolver.Resolver, colliders[interest])...)
}
if len(cols) > 0 {
c := &Context{
typ: resolveContext,
Collisions: cols,
Engine: eng,
}
e.wg.Add(1)
resolver.input <- c
}
}
e.wg.Wait()
}
var ( var (
fullPageIndexes = func() [MaxVertices]uint16 { fullPageIndexes = func() [MaxVertices]uint16 {
@ -394,7 +321,7 @@ var (
}() }()
defaultPageImg = func() *Image { defaultPageImg = func() *Image {
img := NewImage(1, 1) img := NewImage(1, 1)
img.Set(0, 0, Rgba(1, 1, 1, 1)) img.Set(0, 0, RGBA(1, 1, 1, 1))
return img return img
}() }()
defaultTriOptions = &ebiten.DrawTrianglesOptions{} defaultTriOptions = &ebiten.DrawTrianglesOptions{}
@ -423,15 +350,20 @@ func (e *engine) Draw(img *ebiten.Image) {
// First drawing via the inside function // First drawing via the inside function
// and then the returned []EVertex. // and then the returned []EVertex.
layers := maps.NewSparse[Layer, []Drawer](nil, m) layers := maps.NewSparse[Layer, []Drawer](nil, m)
c := &Context{Engine: eng, typ: drawContext, Image: img} c := Context{Engine: eng, Image: img}
for layer := range layers.Chan() { for layer := range layers.Chan() {
vertices := []EVertex{}
vertices := []EVertice{}
for _, drawer := range layer { for _, drawer := range layer {
vs := drawer.Draw(c) drawing := drawer.Draw(c)
if vs != nil { if drawing.EVertices != nil {
vertices = append(vertices, vs...) vertices = append(
vertices,
drawing.EVertices...,
)
} }
} }
pn := len(vertices) / MaxVertices pn := len(vertices) / MaxVertices
mod := len(vertices) % MaxVertices mod := len(vertices) % MaxVertices
for i := 0 ; i<pn ; i++ { for i := 0 ; i<pn ; i++ {
@ -443,6 +375,7 @@ func (e *engine) Draw(img *ebiten.Image) {
defaultTriOptions, defaultTriOptions,
) )
} }
st := pn*MaxVertices st := pn*MaxVertices
img.DrawTriangles( img.DrawTriangles(
vertices[st:], vertices[st:],
@ -467,36 +400,45 @@ func (e *engine) Layout(ow, oh int) (int, int) {
return ow, oh return ow, oh
} }
// Return the delta time. // Return the delta time between Draw calls.
func (e *Engine) DrawDt() Duration { func (e *Engine) DrawDT() Duration {
return e.drawdt return e.drawdt
} }
// Current Drawing frame.
func (e *Engine) Dframe() uint { func (e *Engine) Dframe() uint {
return e.dframe return e.dframe
} }
// Return the current fixed delta time. // Return the real delta time.
func (e *Engine) RealDt() Duration { // Please, prefer the DT().
func (e *Engine) RealDT() Duration {
return e.dt return e.dt
} }
func (e *Engine) Dt() Duration { // Returns the current delta time. (btw frames)
// (By the Ebiten community convention
// currently it is a fixed value)
func (e *Engine) DT() Duration {
return time.Second/60 return time.Second/60
} }
// Current frame
func (e *Engine) Frame() uint { func (e *Engine) Frame() uint {
return e.frame return e.frame
} }
func (e *Engine) Fps() float64 { // Current FPS.
func (e *Engine) FPS() float64 {
return ebiten.ActualFPS() return ebiten.ActualFPS()
} }
func (e *Engine) Tps() float64 { // Current TPS.
func (e *Engine) TPS() float64 {
return ebiten.ActualTPS() return ebiten.ActualTPS()
} }
// Run the engine.
func (e *Engine) Run() error { func (e *Engine) Run() error {
ebiten.ReadDebugInfo(&e.wcfg.DebugInfo) ebiten.ReadDebugInfo(&e.wcfg.DebugInfo)
ebiten.SetWindowTitle(e.wcfg.Title) ebiten.SetWindowTitle(e.wcfg.Title)

View file

@ -4,6 +4,8 @@ import (
//"github.com/hajimehoshi/ebiten/v2" //"github.com/hajimehoshi/ebiten/v2"
) )
import "surdeus.su/core/gg/mx"
func diffEm[V comparable](s1, s2 []V) []V { func diffEm[V comparable](s1, s2 []V) []V {
combinedSlice := append(s1, s2...) combinedSlice := append(s1, s2...)
dm := make(map[V]int) dm := make(map[V]int)
@ -25,31 +27,44 @@ func diffEm[V comparable](s1, s2 []V) []V {
return retSlice return retSlice
} }
// The type represents event of a
// key getting pressed down.
type KeyDown struct { type KeyDown struct {
Key Key
} }
// The type represents event of a
// key getting pressed up.
type KeyUp struct { type KeyUp struct {
Key Key
} }
// The type represents event of a
// mouse button being pressed down.
type MouseButtonDown struct { type MouseButtonDown struct {
MouseButton MouseButton
P Vector Position mx.Vector
} }
// The type represents event of a
// mouse button being pressed up.
type MouseButtonUp struct { type MouseButtonUp struct {
MouseButton MouseButton
P Vector Positon mx.Vector
} }
// The type represents
// event of moving the mouse.
type MouseMove struct { type MouseMove struct {
// Real and absolute deltas. // Real and absolute deltas
Real, Abs Vector // for the mouse movement.
RealDelta, AbsDelta mx.Vector
} }
// The type represents event
// of a wheel change.
type WheelChange struct { type WheelChange struct {
Offset Vector Offset mx.Vector
} }
type EventChan chan any type EventChan chan any

2
go.mod
View file

@ -7,7 +7,7 @@ toolchain go1.21.3
require ( require (
github.com/hajimehoshi/ebiten/v2 v2.6.3 github.com/hajimehoshi/ebiten/v2 v2.6.3
golang.org/x/image v0.15.0 golang.org/x/image v0.15.0
vultras.su/core/gods v0.0.0-20240106154104-c280608f8de4 surdeus.su/core/gods v0.0.1
) )
require ( require (

6
go.sum
View file

@ -1,7 +1,5 @@
github.com/ebitengine/purego v0.5.1 h1:hNunhThpOf1vzKl49v6YxIsXLhl92vbBEv1/2Ez3ZrY= github.com/ebitengine/purego v0.5.1 h1:hNunhThpOf1vzKl49v6YxIsXLhl92vbBEv1/2Ez3ZrY=
github.com/ebitengine/purego v0.5.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/ebitengine/purego v0.5.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
github.com/hajimehoshi/bitmapfont/v3 v3.0.0 h1:r2+6gYK38nfztS/et50gHAswb9hXgxXECYgE8Nczmi4=
github.com/hajimehoshi/bitmapfont/v3 v3.0.0/go.mod h1:+CxxG+uMmgU4mI2poq944i3uZ6UYFfAkj9V6WqmuvZA=
github.com/hajimehoshi/ebiten/v2 v2.6.3 h1:xJ5klESxhflZbPUx3GdIPoITzgPgamsyv8aZCVguXGI= github.com/hajimehoshi/ebiten/v2 v2.6.3 h1:xJ5klESxhflZbPUx3GdIPoITzgPgamsyv8aZCVguXGI=
github.com/hajimehoshi/ebiten/v2 v2.6.3/go.mod h1:TZtorL713an00UW4LyvMeKD8uXWnuIuCPtlH11b0pgI= github.com/hajimehoshi/ebiten/v2 v2.6.3/go.mod h1:TZtorL713an00UW4LyvMeKD8uXWnuIuCPtlH11b0pgI=
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
@ -18,5 +16,5 @@ golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
vultras.su/core/gods v0.0.0-20240106154104-c280608f8de4 h1:aMyoPvgiqs8Gv+4rqcBJR3oBklwF7sAmYyznZ76Jbgs= surdeus.su/core/gods v0.0.1 h1:0hnecGIfWSUG2q2Y9RSuXuAZLD2FCGAvI3lB/xU511Y=
vultras.su/core/gods v0.0.0-20240106154104-c280608f8de4/go.mod h1:EzUklaLgsWmv/+qXUFYfhX+yd2qdc7nG+FoWclgplZ4= surdeus.su/core/gods v0.0.1/go.mod h1:qf/FOAiSKLwVxmHj3EwCgvqmu9DeYAY4AmVh071E9zc=

30
img.go
View file

@ -7,35 +7,35 @@ import (
"io" "io"
"math" "math"
) )
import "surdeus.su/core/gg/mx"
type Image = ebiten.Image type Image = ebiten.Image
type ImageRect = image.Rectangle type ImageRect = image.Rectangle
type ImagePoint = image.Point type ImagePoint = image.Point
type ImageRectIndex struct {
X, Y int
}
type ColorV uint32 type ColorValue uint32
type ColorM = ebiten.ColorM type ColorMode = ebiten.ColorM
type Color struct { type Color struct {
R, G, B, A ColorV R, G, B, A ColorValue
} }
const ( const (
MaxColorV = math.MaxUint32 MaxColorValue = math.MaxUint32
) )
// The wrapper to make RGBA color via // The wrapper to make RGBA color via
// values from 0 to 1 (no value at all and the max value). // values from 0 to 1 (no value at all and the max value).
func Rgba(r, g, b, a Float) Color { func RGBA(r, g, b, a mx.Float) Color {
return Color { return Color {
ColorV(r*MaxColorV), ColorValue(r*MaxColorValue),
ColorV(g*MaxColorV), ColorValue(g*MaxColorValue),
ColorV(b*MaxColorV), ColorValue(b*MaxColorValue),
ColorV(a*MaxColorV), ColorValue(a*MaxColorValue),
} }
} }
// Read an image from Reader and return
// a new *Image.
func LoadImage(input io.Reader) (*Image, error) { func LoadImage(input io.Reader) (*Image, error) {
img, _, err := image.Decode(input) img, _, err := image.Decode(input)
if err != nil { if err != nil {
@ -46,12 +46,18 @@ func LoadImage(input io.Reader) (*Image, error) {
return ret, nil return ret, nil
} }
// Returns a new empty image
// with specified with and height.
func NewImage(w, h int) (*Image) { func NewImage(w, h int) (*Image) {
return ebiten.NewImage(w, h) return ebiten.NewImage(w, h)
} }
func NewImageFromImage(img image.Image) *Image {
return ebiten.NewImageFromImage(img)
}
func (c Color) RGBA() (r, g, b, a uint32) { func (c Color) RGBA() (r, g, b, a uint32) {
return uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A) return uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
} }

56
keep.go Normal file
View file

@ -0,0 +1,56 @@
package gg
/*func (e *engine) Resolve() {
eng := (*Engine)(e)
type resHold struct{
Resolver Resolver
input chan *Context
}
colliders := map[CollisionType] []Collider{}
resolvers := []resHold{}
for _, object := range e.Objects.store {
if object.IsResolvable() {
interests := object.GetCollisionInterest()
for _, interest := range interests {
_, ok := colliders[interest]
if !ok {
colliders[interest] = []Collider{}
}
}
resolvers = append(resolvers, resHold{
object,
object.Input(),
})
}
}
for _, object := range e.Objects.store {
if object.IsCollidable() {
typ := object.CollisionType()
_, ok := colliders[typ]
if !ok {
continue
}
colliders[typ] = append(colliders[typ], object)
}
}
for _, resolver := range resolvers {
interests := resolver.Resolver.GetCollisionInterest()
cols := []Collision{}
for _, interest := range interests {
cols = append(cols, GetCollisions(resolver.Resolver, colliders[interest])...)
}
if len(cols) > 0 {
c := &Context{
typ: resolveContext,
Collisions: cols,
Engine: eng,
}
e.wg.Add(1)
resolver.input <- c
}
}
e.wg.Wait()
}*/

51
line.go
View file

@ -1,51 +0,0 @@
package gg
//import "fmt"
// The type represents mathematical equation of line and line itself.
type Line struct {
K, C, X Float
Vertical bool
}
// Returns the line itself. Made to implement the Liner interface.
func (l Line) Line() Line {
return l
}
func (l Line) ContainsPoint(p Point) bool {
buf := Line{0, p.Y, 0, false}
pc, ok := l.crossesLine(buf)
if !ok {
return false
}
//Println("points:", l, p, pc)
return Neq(pc.X, p.X) && Neq(pc.Y, p.Y)
}
func (l1 Line) crossesLine(l2 Line) (Point, bool) {
var x, y Float
if LinersParallel(l1, l2) {
return Point{}, false
}
switch {
case l1.Vertical :
x = l1.X
y = l2.K*x + l2.C
//Println("l1-vert:", x, y)
case l2.Vertical :
x = l2.X
y = l1.K*x + l1.C
//Println("l2-vert:", x, y)
default:
x = (l1.C - l2.C) / (l2.K - l1.K)
y = l1.K*x + l1.C
}
//Println("in:", x, y)
return Point{x, y}, true
}

View file

@ -1,55 +0,0 @@
package gg
type Liner interface {
Line() Line
}
type LinerPointContainer interface {
Liner
PointContainer
}
// The type represents the cross of 2 Liners.
type LineCross struct {
Pair [2]Line
Point Point
}
// Check if two LinerPointContainers do cross and return the
// crossing point.
func LinersCross(lp1, lp2 LinerPointContainer) (LineCross, bool) {
l1 := lp1.Line()
l2 := lp2.Line()
crossPt, doCross := l1.crossesLine(l2)
if len(lp1.ContainedPoints([]Point{crossPt}))==0 ||
len(lp2.ContainedPoints([]Point{crossPt}))==0 {
doCross = false
}
return LineCross{
Pair: [2]Line{l1, l2},
Point: crossPt,
}, doCross
}
// Check whether the liner is parallel to the other liner.
func LinersParallel(first, second Liner) bool {
l1 := first.Line()
l2 := second.Line()
return l1.K == l2.K || l1.Vertical && l2.Vertical
}
// Returns angle between liners in radians.
// The value fits the -Pi < Value < Pi condition.
func LinersAngle(first, second Liner) Float {
l1 := first.Line()
l2 := second.Line()
if l1.K == l2.K {
return 0
}
return Atan(l1.K/l2.K)
}

32
mx/circle.go Normal file
View file

@ -0,0 +1,32 @@
package mx
// The type describes a simple
// math circle.
type Circle struct {
Center Vector
Radius Float
}
func (c Circle) ContainsPoint(
pt Vector,
) bool {
d := c.Center.Sub(pt)
dx, dy := d.XY()
d2 := dx*dx + dy*dy
r2 := c.Radius*c.Radius
return r2 > d2
}
func (c Circle) GetContainedPoints(
pts Vectors,
) Vectors {
ret := make(Vectors, 0, len(pts))
for _, pt := range pts {
if c.ContainsPoint(pt) {
ret = append(ret, pt)
}
}
return ret
}

View file

@ -1,7 +1,7 @@
package gg package mx
// The structure represents elipses. // The structure represents elipses.
type Elipse struct { /*type Elipse struct {
// In transform S.X and S.Y represent // In transform S.X and S.Y represent
// coefficents for the corresponding axises. // coefficents for the corresponding axises.
Transform Transform
@ -9,5 +9,5 @@ type Elipse struct {
func (e Elipse) ContainsPoint(p Point) bool { func (e Elipse) ContainsPoint(p Point) bool {
return true return true
} }*/

View file

@ -1,26 +1,11 @@
package gg package mx
type Edges []Edge
// Get crosses of edges.
func (what Edges) CrossWithEdges(with Edges) ([]LineCross, bool) {
ret := []LineCross{}
for i := range what {
for j := range with {
cross, doCross := LinersCross(what[i], with[j])
if doCross {
ret = append(ret, cross)
}
}
}
return ret, len(ret) > 0
}
// The type represents a line segment. The name is for short. // The type represents a line segment. The name is for short.
type Edge [2]Vector type Line [2]Vector
type Lines []Line
// Returns corresponding to the segment Line. // Returns corresponding to the segment Line.
func (l Edge) Line() Line { func (l Line) GetLineExpr() LineExpr {
var ( var (
x Float x Float
vertical bool vertical bool
@ -36,11 +21,12 @@ func (l Edge) Line() Line {
vertical = true vertical = true
} }
return Line{k, c, x, vertical} return LineExpr{k, c, x, vertical}
} }
func (l Edge) ContainedPoints(pts Points) (Points) { // Returns the points that the Line contains.
ret := Points{} func (l Line) GetContainedPoints(pts Vectors) Vectors {
ret := make(Vectors, 0, len(pts))
for i := range pts { for i := range pts {
if l.ContainsPoint(pts[i]) { if l.ContainsPoint(pts[i]) {
ret = append(ret, pts[i]) ret = append(ret, pts[i])
@ -50,9 +36,9 @@ func (l Edge) ContainedPoints(pts Points) (Points) {
} }
// Check if the edge contains the specified point. // Check if the edge contains the specified point.
func (l Edge) ContainsPoint(p Point) bool { func (l Line) ContainsPoint(p Vector) bool {
line := l.Line() lexpr := l.GetLineExpr()
if !line.ContainsPoint(p) { if !lexpr.ContainsPoint(p) {
return false return false
} }
@ -71,13 +57,13 @@ func (l Edge) ContainsPoint(p Point) bool {
} }
// Get square of length of line segment (for performance sometimes). // Get square of length of line segment (for performance sometimes).
func (ls Edge) LenSqr() Float { func (ls Line) LenSqr() Float {
return Sqr(ls[0].X - ls[1].X) + return Sqr(ls[0].X - ls[1].X) +
Sqr(ls[0].Y - ls[1].Y) Sqr(ls[0].Y - ls[1].Y)
} }
// Get length of the line segment. // Get length of the line segment.
func (ls Edge) Len() Float { func (ls Line) Len() Float {
return Sqrt(ls.LenSqr()) return Sqrt(ls.LenSqr())
} }

61
mx/line_expr.go Normal file
View file

@ -0,0 +1,61 @@
package mx
//import "fmt"
// The type represents mathematical equation of line and line itself.
type LineExpr struct {
K, C, X Float
Vertical bool
}
// Returns the line itself. Made to implement the Liner interface.
func (l LineExpr) GetLineExpr() LineExpr {
return l
}
func (e LineExpr) GetContainedPoints(pts Vectors) Vectors {
ret := make(Vectors, 0, len(pts))
for _, pt := range pts {
if e.ContainsPoint(pt) {
ret = append(ret, pt)
}
}
return ret
}
func (l LineExpr) ContainsPoint(p Vector) bool {
buf := LineExpr{0, p.Y, 0, false}
pc, ok := DoLinersCross(l, buf)
if !ok {
return false
}
//Println("points:", l, p, pc)
return Neq(pc.Point.X, p.X) && Neq(pc.Point.Y, p.Y)
}
func (l1 LineExpr) Crosses(l2 LineExpr) (Vector, bool) {
var x, y Float
if AreLinersParallel(l1, l2) {
return ZV, false
}
switch {
case l1.Vertical :
x = l1.X
y = l2.K*x + l2.C
//Println("l1-vert:", x, y)
case l2.Vertical :
x = l2.X
y = l1.K*x + l1.C
//Println("l2-vert:", x, y)
default:
x = (l1.C - l2.C) / (l2.K - l1.K)
y = l1.K*x + l1.C
}
//Println("in:", x, y)
return Vector{x, y}, true
}

81
mx/liner.go Normal file
View file

@ -0,0 +1,81 @@
package mx
type Liner interface {
PointContainer
GetLineExpr() LineExpr
}
type Liners []Liner
// The type represents the cross of 2 Liners.
type LinersCrossing struct {
Pair [2]LineExpr
Point Vector
}
// Checks all possible combinations
// of Liners to cross and returns
// the crossings if are present.
func GetLinersCrossings(
what, with Liners,
) []LinersCrossing {
ret := make([]LinersCrossing, 0, len(with))
for i := range what {
for j := range with {
cross, doCross := DoLinersCross(
what[i],
with[j],
)
if doCross {
ret = append(ret, cross)
}
}
}
return ret
}
// Check if two LinerPointContainers do cross and return the
// crossing point.
func DoLinersCross(
lp1, lp2 Liner,
) (LinersCrossing, bool) {
l1 := lp1.GetLineExpr()
l2 := lp2.GetLineExpr()
crossPt, doCross := l1.Crosses(l2)
if !lp1.ContainsPoint(crossPt) ||
!lp2.ContainsPoint(crossPt) {
return LinersCrossing{}, false
}
return LinersCrossing{
Pair: [2]LineExpr{l1, l2},
Point: crossPt,
}, doCross
}
// Check whether the liner is parallel to the other liner.
func AreLinersParallel(
first, second Liner,
) bool {
l1 := first.GetLineExpr()
l2 := second.GetLineExpr()
return l1.Vertical && l2.Vertical ||
l1.K == l2.K
}
// Returns angle between liners in radians.
// The value fits the -Pi < Value < Pi condition.
func GetAngleBetweenLiners(
first, second Liner,
) Float {
l1 := first.GetLineExpr()
l2 := second.GetLineExpr()
if l1.K == l2.K {
return 0
}
return Atan(l1.K/l2.K)
}

View file

@ -1,11 +1,13 @@
package gg package mx
import ( import (
"math" "math"
) )
// The type is used in all Engine interactions // The type is used in all Engine interactions
// where you need floating values. // where you need floating values to keep
// it interchangable in future.
// Not much promise for this though.
type Float = float64 type Float = float64
const ( const (

View file

@ -1,9 +1,8 @@
package gg package mx
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
//"math"
) )
type Matrix = ebiten.GeoM type Matrice = ebiten.GeoM

10
mx/point.go Normal file
View file

@ -0,0 +1,10 @@
package mx
// The type provides interface
// to check if an objects
// contains points.
type PointContainer interface {
GetContainedPoints(Vectors) Vectors
ContainsPoint(Vector) bool
}

View file

@ -1,10 +1,10 @@
package gg package mx
// The type represents math ray. // The type represents math ray.
type Ray struct { type Ray struct {
// The start point. // The start point.
P Point Start Vector
// Rotation of the ray. // Rotation of the ray.
R Float Rotaton Float
} }

View file

@ -1,16 +1,16 @@
package gg package mx
// The type describes a simple // The type describes a simple
// rectangle without transformations. // rectangle without transformations.
type Size struct { type Size struct {
// The upper left corner position point. // The upper left corner position point.
Position Point Position Vector
// Absolute width and height. // Absolute width and height.
// Both must be positive values. // Both must be positive values.
Width, Height Float Width, Height Float
} }
func (size Size) ContainsPoint(pt Point) bool { func (size Size) ContainsPoint(pt Vector) bool {
return (size.Position.X < pt.X && (size.Position.X + size.Width) > pt.X ) && return (size.Position.X < pt.X && (size.Position.X + size.Width) > pt.X ) &&
(size.Position.Y < pt.Y && (size.Position.Y + size.Height) > pt.Y ) (size.Position.Y < pt.Y && (size.Position.Y + size.Height) > pt.Y )
} }

106
mx/triangle.go Normal file
View file

@ -0,0 +1,106 @@
package mx
import (
"math"
//"github.com/hajimehoshi/ebiten/v2"
)
type Triangle [3]Vector
// Returns the area of the triangle.
func (t Triangle) Area() Float {
x1 := t[0].X
y1 := t[0].Y
x2 := t[1].X
y2 := t[1].Y
x3 := t[2].X
y3 := t[2].Y
return math.Abs(
( x1*(y2-y3) +
x2*(y3-y1) +
x3*(y1-y2))/2 )
}
// Return squares of lengths of sides of the triangle.
func (t Triangle) SideLengthSquares() ([3]Float) {
l1 := Line{t[0], t[1]}.LenSqr()
l2 := Line{t[1], t[2]}.LenSqr()
l3 := Line{t[2], t[0]}.LenSqr()
return [3]Float{l1, l2, l3}
}
// Check whether the point is in the triangle.
func (t Triangle) ContainsPoint(p Vector) bool {
d1 := Triangle{p, t[0], t[1]}.Sgn()
d2 := Triangle{p, t[1], t[2]}.Sgn()
d3 := Triangle{p, t[2], t[0]}.Sgn()
neg := (d1 < 0) || (d2 < 0) || (d3 < 0)
pos := (d1 > 0) || (d2 > 0) || (d3 > 0)
return !(neg && pos)
}
func (t Triangle) GetContainedPoints(pts Vectors) Vectors {
ret := make(Vectors, 0, len(pts))
for i := range pts {
if t.ContainsPoint(pts[i]) {
ret = append(ret, pts[i])
}
}
return ret
}
func (t Triangle) Sgn() Float {
return (t[0].X - t[2].X) * (t[1].Y - t[2].Y) -
(t[1].X - t[2].X) * (t[0].Y - t[2].Y)
}
func (t Triangle) GetEdges() Lines {
return Lines{
Line{t[0], t[1]},
Line{t[1], t[2]},
Line{t[2], t[0]},
}
}
func (t Triangle) GetVertices() Vectors {
return Vectors{
t[0], t[1], t[2],
}
}
type Triangles []Triangle
func (ts Triangles) GetContainedPoints(
pts Vectors,
) Vectors {
ret := Vectors{}
for _, t := range ts {
ps := t.GetContainedPoints(pts)
ret = append(ret, ps...)
}
return ret
}
func (ts Triangles) GetVertices() Vectors {
ret := make(Vectors, 0, len(ts)*3)
for _, t := range ts {
ret = append(ret, t.GetVertices()...)
}
return ret
}
func (ts Triangles) GetEdges() Lines {
ret := make(Lines, 0, len(ts)*3)
for _, t := range ts {
ret = append(ret, t.GetEdges()...)
}
return ret
}

View file

@ -1,4 +1,4 @@
package gg package mx
import ( import (
//"github.com/hajimehoshi/ebiten/v2" //"github.com/hajimehoshi/ebiten/v2"
@ -11,11 +11,13 @@ var (
type Vector struct { type Vector struct {
X, Y Float X, Y Float
} }
type Vectors []Vector
func (v Vector) XY() (Float, Float) { func (v Vector) XY() (Float, Float) {
return v.X, v.Y return v.X, v.Y
} }
type Vectors []Vector
func V(x, y Float) Vector { func V(x, y Float) Vector {
return Vector{x, y} return Vector{x, y}
@ -25,11 +27,11 @@ func V2(v Float) Vector {
return Vector{v, v} return Vector{v, v}
} }
func X(x Float) Vector { func VX(x Float) Vector {
return Vector{x, 0} return Vector{x, 0}
} }
func Y(y Float) Vector { func VY(y Float) Vector {
return Vector{0, y} return Vector{0, y}
} }
@ -52,27 +54,23 @@ func (v Vector) Eq(o Vector) bool {
} }
// Returns the vector with the matrix applied // Returns the vector with the matrix applied
func (v Vector) Apply(m Matrix) Vector { func (v Vector) Apply(m Matrice) Vector {
x, y := m.Apply(v.X, v.Y) x, y := m.Apply(v.X, v.Y)
return Vector{x, y} return Vector{x, y}
} }
// Adds the vector to other one returning the result. // Adds the vector to other one returning the result.
func (v Vector) Add(a ...Vector) Vector { func (v Vector) Add(a Vector) Vector {
for _, r := range a { v.X += a.X
v.X += r.X v.Y += a.Y
v.Y += r.Y
}
return v return v
} }
// Returns the subtraction of all the vectors from the current one. // Returns the subtraction of all the vectors from the current one.
func (v Vector) Sub(s ...Vector) Vector { func (v Vector) Sub(s Vector) Vector {
for _, r := range s { v.X -= s.X
v.X -= r.X v.Y -= s.Y
v.Y -= r.Y
}
return v return v
} }
@ -87,7 +85,7 @@ func (v Vector) Neg() Vector {
// Returns the vector rotated by "a" angle in radians. // Returns the vector rotated by "a" angle in radians.
func (v Vector) Rot(a Float) Vector { func (v Vector) Rot(a Float) Vector {
m := Matrix{} m := Matrice{}
m.Rotate(a) m.Rotate(a)
return v.Apply(m) return v.Apply(m)
} }
@ -98,11 +96,13 @@ func (v Vector) Norm() Vector {
return V(v.X / l, v.Y / l) return V(v.X / l, v.Y / l)
} }
func (pts Points) ContainedIn(c PointContainer) Points { func (pts Vectors) PointsContainedIn(
ret := make([]Point, 0, len(pts)) c PointContainer,
) Vectors {
ret := make(Vectors, 0, len(pts))
for _, pt := range pts { for _, pt := range pts {
if !c.ContainedPoints(Points{pt}).Empty() { if c.ContainsPoint(pt) {
ret = append(ret, pt) ret = append(ret, pt)
} }
} }
@ -110,7 +110,3 @@ func (pts Points) ContainedIn(c PointContainer) Points {
return ret return ret
} }
func (pts Points) Len() int {
return len(pts)
}

2
mx/vertice.go Normal file
View file

@ -0,0 +1,2 @@
package mx

View file

@ -1,88 +1,14 @@
package gg package gg
// Implementing the interface type // The interface must
// will call the function OnStart // me implemented for all the
// when first appear on scene BEFORE // in-game logic objects.
// the OnUpdate. type Object interface {
// The v value will be get from Add function.
type Starter interface {
Start(*Context)
}
// Implementing the interface type
// will call the function on each
// engine iteration.
type Updater interface {
Update(*Context)
}
// Implementing the interface type
// will call the function on deleting
// the object.
type Deleter interface {
Delete(*Context)
}
type Antialiasity struct {
Antialias bool
}
// Feat to embed for turning visibility on and off.
type Visibility struct {
Visible bool
}
func (v Visibility) IsVisible() bool {
return v.Visible
}
// Feat to embed to make colorful objects.
type Colority struct {
Color Color
}
// The interface describes anything that can be
// drawn. It will be drew corresponding to
// the layers order so the layer must be returned.
type Drawer interface {
Draw(*Context) []EVertex
GetLayer() Layer
IsVisible() bool
}
type Objecter interface {
GetObject() *Object
Input() chan *Context
Starter
Updater
Collider
Resolver
Drawer Drawer
Deleter OnStart(Context)
OnUpdate(Context)
OnDelete(Context)
GetTags() map[string]struct{}
} }
// The type for embedding into engine-in types.
type Object struct {
Collidability
Resolvability
Layer
Visibility
input chan *Context
}
func (o *Object) GetObject() *Object { return o }
func (o *Object) Input() chan *Context { return o.input }
func (o *Object) Start(c *Context) {}
func (o *Object) Update(c *Context) {}
func (o *Object) ContainedPoints(Points) Points {return Points{}}
func (o *Object) Vertices() Vertices {return Vertices{}}
func (o *Object) Edges() Edges {return Edges{}}
func (o *Object) Resolve(c *Context) {}
func (o *Object) Draw(c *Context) []EVertex { return nil}
func (o *Object) Delete(c *Context) {}

56
ox/asprite.go Normal file
View file

@ -0,0 +1,56 @@
package ox
import "surdeus.su/core/gg"
import "surdeus.su/core/gg/ax"
// The type implements animated sprites.
type AnimatedSprite struct {
Sprite
animations ax.AnimationSet
animationId ax.AnimationId
currentFrame int
// This is time between animation frames, not
// engine ones.
timeBetweenFrames gg.Duration
duration gg.Duration
}
func NewAnimatedSprite(
) *AnimatedSprite {
ret := &AnimatedSprite{}
return ret
}
// Change current animation to the specified one.
func (as *AnimatedSprite) Animate(id ax.AnimationId) bool {
if as.AnimationId == id {
return true
}
_, ok := as.Animations[id]
if ok {
as.duration = 0
as.AnimationId = id
}
return ok
}
func (as *AnimatedSprite) Draw(c Context) []EVertex {
as.duration += c.DrawDT()
frames := as.Animations[as.AnimationId]
fullTime := Duration(len(frames)) * as.TimeBetweenFrames
if as.duration > fullTime {
as.duration -= fullTime
}
as.Images[0] = frames[
(as.duration/as.TimeBetweenFrames) %
Duration(len(frames)),
]
return as.Sprite.Draw(c)
}

76
ox/camera.go Normal file
View file

@ -0,0 +1,76 @@
package ox
import "surdeus.su/core/gg"
import "surdeus.su/core/gg/mx"
// Default camera implementation.
type Camera struct {
ObjectImpl
// The Transform to interact with
// to change camera position, rotation etc.
Transform
// The shaders that will be applied to everything
// that the camera shows.
Shaderity
// The bufferization implementation
// to keep everything fast.
absMatrice realMatrice
}
// Returns the new camera
// with default settings.
func NewCamera() *Camera {
ret := &Camera{}
ret.Transform = T()
return ret
}
// Returns the matrix satysfying camera
// position, scale and rotation to apply
// it to the objects to get the real
// transform to display on the screen.
func (c *Camera) GetRealMatrice(ctx gg.Context) mx.Matrice {
// Bufferization
// so we do not need to recalculate
// Transform again.
if !c.Transform.Dirty() {
return c.realMatrice
}
var g mx.Matrice
position := c.GetPosition().Neg()
around := c.GetAround()
scale := c.GetScale()
rotation := c.GetRotation()
g.Translate(-position.X, -position.Y)
g.Rotate(rotation)
size := ctx.Engine.AbsWinSize()
g.Translate(around.X * size.X, around.Y * size.Y)
g.Scale(scale.X, scale.Y)
c.realMatrice = g
return g
}
// The matrix to convert things into the
// inside engine representation,
// get the position of cursor inside the world
// basing on its window position.
func (c *Camera) GetAbsMatrice(ctx gg.Context) mx.Matrice {
if !c.Transform.Dirty() {
return c.absMatrice
}
m := c.GetRealMatrice(ctx)
m.Invert()
c.absMatrice = m
return m
}

View file

@ -1,12 +1,14 @@
package gg package ox
import ( import (
"github.com/hajimehoshi/ebiten/v2/vector" "github.com/hajimehoshi/ebiten/v2/vector"
) )
import "surdeus.su/core/gg"
type Circle struct { type Circle struct {
Object ObjectImpl
Transform gg.Transform
Visibility Visibility
Colority Colority
Antialiasity Antialiasity

31
ox/feats.go Normal file
View file

@ -0,0 +1,31 @@
package ox
// Feat to emded for turning antialias on and off.
type Antialiasity struct {
Antialias bool
}
// Feat to embed for turning visibility on and off.
type Visibility struct {
Visible bool
}
func (v Visibility) IsVisible() bool {
return v.Visible
}
// Feat to embed to make colorful objects.
type Colority struct {
Color Color
}
// The structure to embed into shaderable
// objects.
type Shaderity struct {
ShaderOptions ShaderOptions
}
func (s *Shaderity) GetShaderOptions(
) *ShaderOptions {
return &s.ShaderOptions
}

12
ox/object.go Normal file
View file

@ -0,0 +1,12 @@
package ox
// The standard empty implementation
// of the Object interface to embed.
type ObjectImpl struct {}
func (o ObjectImpl) OnStart(c Context) {}
func (o ObjectImpl) OnUpdate(c Context) {}
func (o ObjectImpl) OnDelete(c Context) {}
func (o ObjectImpl) GetTags() map[string]struct{}{return nil}
func (o ObjectImpl) Draw(c Context) {}
func (o ObjectImpl) GetLayer() Layer {return 0}
func (o ObjectImpl) IsVisible() bool {return false}

View file

@ -1,13 +1,15 @@
package gg package ox
import ( import (
"surdeus.su/core/gg"
"surdeus.su/core/gg/mx"
) )
// Grouped triangles type. // Grouped triangles type.
type Polygon struct { type Polygon struct {
Object ObjectImpl
Transform Transform
Triangles mx.Triangles
} }
func (p Polygon) ContainedPoints(pts Points) (Points) { func (p Polygon) ContainedPoints(pts Points) (Points) {
@ -15,7 +17,7 @@ func (p Polygon) ContainedPoints(pts Points) (Points) {
} }
func (p Polygon) MakeTriangles() Triangles { func (p Polygon) MakeTriangles() Triangles {
m := p.Matrix() m := p.Transform.GetMatrice()
ret := make(Triangles, len(p.Triangles)) ret := make(Triangles, len(p.Triangles))
for i, t := range p.Triangles { for i, t := range p.Triangles {
ret[i] = Triangle{ ret[i] = Triangle{
@ -28,11 +30,12 @@ func (p Polygon) MakeTriangles() Triangles {
return ret return ret
} }
func (p Polygon) Vertices() Vertices { func (p Polygon) GetVertices() mx.Vectors {
return p.MakeTriangles().Vertices() return p.MakeTriangles().GetVertices()
} }
func (p Polygon) Edges() Edges {
return p.MakeTriangles().Edges() func (p Polygon) GetEdges() mx.Lines {
return p.MakeTriangles().GetEdges()
} }
// Polygon that can be drawn. // Polygon that can be drawn.
@ -43,10 +46,13 @@ type DrawablePolygon struct {
Colority Colority
} }
func (p *DrawablePolygon) Draw(c *Context) []EVertex { func (p *DrawablePolygon) Draw(c *Context) gg.Drawing {
return (&DrawableTriangles{ ret := gg.Drawing{
Colority: p.Colority, EVertices: (&DrawableTriangles{
Triangles: p.MakeTriangles(), Colority: p.Colority,
}).MakeEVertices(c) Triangles: p.MakeTriangles(),
}).MakeEVertices(c),
}
return ret
} }

View file

@ -1,4 +1,4 @@
package gg package ox
import ( import (
//"github.com/hajimehoshi/ebiten/v2" //"github.com/hajimehoshi/ebiten/v2"
@ -6,17 +6,19 @@ import (
//"fmt" //"fmt"
//"image" //"image"
) )
import "surdeus.su/core/gg"
import "surdeus.su/core/gg/mx"
// The type describes rectangle geometry with // The type describes rectangle geometry with
// way to move, rotate and scale it. // way to move, rotate and scale it.
type Rectangle struct { type Rectangle struct {
Object ObjectImpl
Transform gg.Transform
Width, Height Float Width, Height Float
} }
// Return points of vertices of the rectangle. // Return points of vertices of the rectangle.
func (r Rectangle) Vertices() Vertices { func (r Rectangle) Vertices() mx.Vectors {
t := r.Transform t := r.Transform
wh := V(r.Width, r.Height) wh := V(r.Width, r.Height)
t.SetAround(t.Around().Mul(wh)) t.SetAround(t.Around().Mul(wh))
@ -25,12 +27,12 @@ func (r Rectangle) Vertices() Vertices {
p2 := V(wh.X, 0).Apply(m) p2 := V(wh.X, 0).Apply(m)
p3 := V(wh.X, wh.Y).Apply(m) p3 := V(wh.X, wh.Y).Apply(m)
p4 := V(0, wh.Y).Apply(m) p4 := V(0, wh.Y).Apply(m)
return Vertices{p1, p2, p3, p4} return mx.Vectors{p1, p2, p3, p4}
} }
func (r Rectangle) Edges() Edges { func (r Rectangle) Edges() mx.Lines {
vs := r.Vertices() vs := r.Vertices()
return Edges{ return Lines{
Edge{vs[0], vs[1]}, Edge{vs[0], vs[1]},
Edge{vs[1], vs[2]}, Edge{vs[1], vs[2]},
Edge{vs[2], vs[3]}, Edge{vs[2], vs[3]},
@ -39,22 +41,24 @@ func (r Rectangle) Edges() Edges {
} }
// Get 2 triangles that the rectangle consists of. // Get 2 triangles that the rectangle consists of.
func (r Rectangle) MakeTriangles() Triangles { func (r Rectangle) MakeTriangles() mx.Triangles {
pts := r.Vertices() pts := r.Vertices()
p1 := pts[0] p1 := pts[0]
p2 := pts[1] p2 := pts[1]
p3 := pts[2] p3 := pts[2]
p4 := pts[3] p4 := pts[3]
return Triangles{ return mx.Triangles{
Triangle{p1, p2, p3}, mx.Triangle{p1, p2, p3},
Triangle{p1, p4, p3}, mx.Triangle{p1, p4, p3},
} }
} }
// Check whether the rectangle contains the point. // Check whether the rectangle contains the point.
func (r Rectangle) ContainedPoints(pts Points) (Points) { func (r Rectangle) GetContainedPoints(
return r.MakeTriangles().ContainedPoints(pts) pts mx.Vectors,
) (mx.Vectors) {
return r.MakeTriangles().GetContainedPoints(pts)
} }
// The type describes rectangle that can be drawn. // The type describes rectangle that can be drawn.

View file

@ -1,13 +1,16 @@
package gg package ox
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
//"fmt" //"fmt"
) )
import "surdeus.su/core/gg"
import "surdeus.su/core/gg/mx"
type Sprite struct { type Sprite struct {
Object ObjectImpl
Transform gg.Transform
ShaderOptions ShaderOptions
Floating bool Floating bool
Visibility Visibility
@ -17,16 +20,16 @@ var (
//spritesOp DrawImageOptions //spritesOp DrawImageOptions
) )
func (s *Sprite) Draw(c *Context) []EVertex { func (s *Sprite) Draw(c Context) []EVertex {
// Nothing to draw. // Nothing to draw.
if s.Images[0] == nil { if s.Images[0] == nil {
return nil return nil
} }
t := s.RectangleToDraw().Transform t := s.GetRectangleToDraw().Transform
m := t.Matrix() m := t.GetMatrice()
if !s.Floating { if !s.Floating {
m.Concat(c.Camera.RealMatrix()) m.Concat(c.Camera.GetRealMatrix())
} }
// Drawing without shader. // Drawing without shader.
@ -39,16 +42,20 @@ func (s *Sprite) Draw(c *Context) []EVertex {
w, h := s.Images[0].Size() w, h := s.Images[0].Size()
// Drawing with shader. // Drawing with shader.
c.DrawRectShader(w, h, s.Shader, &ebiten.DrawRectShaderOptions{ c.DrawRectShader(
Images: s.Images, w, h,
Uniforms: s.Uniforms, s.Shader,
GeoM: m, &ebiten.DrawRectShaderOptions{
}) Images: s.Images,
Uniforms: s.Uniforms,
GeoM: m,
},
)
return nil return nil
} }
// Return the rectangle that contains the sprite to draw. // Return the rectangle that contains the sprite to draw.
func (s *Sprite) RectangleToDraw() Rectangle { func (s *Sprite) GetRectangleToDraw() Rectangle {
if s.Images[0] == nil { if s.Images[0] == nil {
return Rectangle{} return Rectangle{}
} }
@ -66,8 +73,9 @@ func (s *Sprite) RectangleToDraw() Rectangle {
} }
} }
// Return the rectangle that contains the sprite in the engine. // Return the rectangle that contains
func (s *Sprite) Rectangle() Rectangle { // the sprite in the engine.
func (s *Sprite) GetRectangle() Rectangle {
if s.Images[0] == nil { if s.Images[0] == nil {
return Rectangle{ return Rectangle{
Transform: s.Transform, Transform: s.Transform,
@ -90,14 +98,18 @@ func (s *Sprite) MakeTriangles() Triangles {
return s.Rectangle().MakeTriangles() return s.Rectangle().MakeTriangles()
} }
func (s *Sprite) Vertices() Vertices { func (s *Sprite) Vertices() mx.Vectors {
return s.Rectangle().Vertices() return s.GetRectangle().Vertices()
} }
func (s *Sprite) Edges() Edges { func (s *Sprite) Edges() mx.Lines {
return s.Rectangle().Edges() return s.GetRectangle().Edges()
} }
func (s *Sprite) ContainedPoints(pts Points) Points { func (s *Sprite) ContainsPoint(pt mx.Vector) bool {
return s.Rectangle().ContainedPoints(pts) return s.GetRectangle().ContainsPoint(pt)
}
func (s *Sprite) GetContainedPoints(pts mx.Vectors) Points {
return s.Rectangle().GetContainedPoints(pts)
} }

28
ox/text.go Normal file
View file

@ -0,0 +1,28 @@
package ox
import "surdeus.su/core/gg/mx"
import "surdeus.su/core/gg"
// The type implements basic drawable text.
// (Now needs to implement rotation, scaling etc, cause now only position)
type Text struct {
ObjectImpl
mx.Transform
Data string
Face Face
Colority
Visibility
}
func (txt *Text) Draw(c gg.Context) []EVertex {
m := txt.Matrix()
m.Concat(c.Camera.RealMatrix())
//x, y := txt.Position.XY()
//text.Draw(c.Image)
text.DrawWithOptions(c.Image, txt.Data, txt.Face,
&ebiten.DrawImageOptions{
GeoM: m,
},
)
return nil
}

View file

@ -1,25 +1,23 @@
package gg package ox
import ( import "surdeus.su/core/gg/mx"
//"github.com/hajimehoshi/ebiten/v2"
//"math"
)
type Transformer interface { type Transformer interface {
GetTransform() *Transform GetTransform() *Transform
} }
// The structure represents basic transformation // The structure represents basic transformation
// features: positioning, rotating and scaling. // features: positioning, rotating and scaling
// in more high level and more "GAME ENGINE" style.
type Transform struct { type Transform struct {
// Absolute (if no parent) position and // Absolute (if no parent) position and
// the scale. // the scale.
position, scale Vector position, scale mx.Vector
// The object rotation in radians. // The object rotation in radians.
rotation Float rotation mx.Float
// The not scaled offset vector from upper left corner // The not scaled offset vector from upper left corner
// which the object should be rotated around. // which the object should be rotated around.
around Vector around mx.Vector
// If is not nil then the upper values will be relational to // If is not nil then the upper values will be relational to
// the parent ones. // the parent ones.
@ -28,7 +26,24 @@ type Transform struct {
// Dirty is true if we anyhow changed matrix. // Dirty is true if we anyhow changed matrix.
dirty, parentDirty bool dirty, parentDirty bool
matrix, parentMatrix, parentInverted Matrix matrix, parentMatrice, parentInverted mx.Matrice
}
// Returns the default Transform structure.
func T() Transform {
ret := Transform{
// Rotate around
scale: mx.V2(1),
// Rotate around the center.
around: mx.V2(.5),
}
return ret
}
// Returns whether it needs to be recalculated
// or not.
func (t *Transform) IsDirty() bool {
return t.dirty
} }
// For implementing the Transformer on embedding. // For implementing the Transformer on embedding.
@ -36,19 +51,8 @@ func (t *Transform) GetTransform() *Transform {
return t return t
} }
// Returns the default Transform structure.
func T() Transform {
ret := Transform{
// Rotate around
scale: Vector{1, 1},
// Rotate around the center.
around: V(.5, .5),
}
return ret
}
// Set the absolute object position. // Set the absolute object position.
func (t *Transform) SetPosition(position Vector) { func (t *Transform) SetPosition(position mx.Vector) {
t.dirty = true t.dirty = true
t.parentDirty = true t.parentDirty = true
if t.parent == nil { if t.parent == nil {
@ -56,35 +60,35 @@ func (t *Transform) SetPosition(position Vector) {
return return
} }
_, mi := t.parent.MatrixForParenting() _, mi := t.parent.GetMatriceForParenting()
t.position = position.Apply(mi) t.position = position.Apply(mi)
} }
// Set the absolute object rotation. // Set the absolute object rotation.
func (t *Transform) SetRotation(rotation Float) { func (t *Transform) SetRotation(rotation mx.Float) {
t.dirty = true t.dirty = true
t.parentDirty = true t.parentDirty = true
if t.parent == nil { if t.parent == nil {
t.rotation = rotation t.rotation = rotation
return return
} }
t.rotation -= t.parent.Rotation() t.rotation -= t.parent.GetRotation()
} }
// Set the absolute object scale. // Set the absolute object scale.
func (t *Transform) SetScale(scale Vector) { func (t *Transform) SetScale(scale mx.Vector) {
t.dirty = true t.dirty = true
t.parentDirty = true t.parentDirty = true
t.scale = scale t.scale = scale
} }
func (t *Transform) AddScale(add ...Vector) { func (t *Transform) AddScale(add mx.Vector) {
t.dirty = true t.dirty = true
//t.parentDirty = true //t.parentDirty = true
t.scale = t.scale.Add(add...) t.scale = t.scale.Add(add)
} }
func (t *Transform) SetAround(around Vector) { func (t *Transform) SetAround(around mx.Vector) {
t.dirty = true t.dirty = true
t.around = around t.around = around
} }
@ -96,10 +100,10 @@ func (t *Transform) Abs() Transform {
} }
ret := Transform{} ret := Transform{}
ret.position = t.Position() ret.position = t.GetPosition()
ret.rotation = t.Rotation() ret.rotation = t.GetRotation()
ret.scale = t.Scale() ret.scale = t.GetScale()
ret.around = t.Around() ret.around = t.GetAround()
ret.dirty = true ret.dirty = true
ret.parentDirty = true ret.parentDirty = true
@ -113,43 +117,44 @@ func (t *Transform) Rel() Transform {
} }
// Get the absolute object position. // Get the absolute object position.
func (t *Transform) Position() Vector { func (t *Transform) GetPosition() mx.Vector {
if t.parent == nil { if t.parent == nil {
return t.position return t.position
} }
pm, _ := t.parent.MatrixForParenting() pm, _ := t.parent.GetMatriceForParenting()
return t.position.Apply(pm) return t.position.Apply(pm)
} }
func (t *Transform) Move(v ...Vector) { // Move by the specified delta.
t.SetPosition(t.Position().Add(v...)) func (t *Transform) Move(v mx.Vector) {
t.SetPosition(t.GetPosition().Add(v))
} }
// Get the absolute object scale. // Get the absolute object scale.
func (t *Transform) Scale() Vector { func (t *Transform) GetScale() mx.Vector {
return t.scale return t.scale
} }
// Get the absolute object rotation. // Get the absolute object rotation.
func (t *Transform) Rotation() Float { func (t *Transform) GetRotation() mx.Float {
if t.parent == nil { if t.parent == nil {
return t.rotation return t.rotation
} }
return t.rotation + t.parent.Rotation() return t.rotation + t.parent.GetRotation()
} }
func (t *Transform) Rotate(add Float) { func (t *Transform) Rotate(rot mx.Float) {
t.dirty = true t.dirty = true
t.parentDirty = true t.parentDirty = true
t.rotation += add t.rotation += rot
} }
func (t *Transform) Around() Vector { func (t *Transform) GetAround() mx.Vector {
return t.around return t.around
} }
// Returns true if the object is connected // Returns true if the object is connected
// to some parent. // to some parent.
func (t *Transform) Connected() bool { func (t *Transform) IsConnected() bool {
return t.parent != nil return t.parent != nil
} }
@ -163,8 +168,8 @@ func (t *Transform) Connect(parent Transformer) {
t.Disconnect() t.Disconnect()
} }
position := t.Position() position := t.GetPosition()
rotation := t.Rotation() rotation := t.GetRotation()
t.parent = parent.GetTransform() t.parent = parent.GetTransform()
t.SetPosition(position) t.SetPosition(position)
@ -180,14 +185,15 @@ func (t *Transform) Disconnect() {
} }
// Return the matrix and the inverted one for parenting children. // Return the matrix and the inverted one for parenting children.
func (t *Transform) MatrixForParenting() (Matrix, Matrix) { func (t *Transform) GetMatriceForParenting(
var m, mi Matrix ) (mx.Matrice, mx.Matrice) {
var m, mi mx.Matrice
if t.parentDirty { if t.parentDirty {
//m.Scale(t.scale.X, t.scale.Y) //m.Scale(t.scale.X, t.scale.Y)
m.Rotate(t.rotation) m.Rotate(t.rotation)
m.Translate(t.position.X, t.position.Y) m.Translate(t.position.X, t.position.Y)
t.parentMatrix = m t.parentMatrice = m
mi = m mi = m
mi.Invert() mi.Invert()
@ -195,26 +201,25 @@ func (t *Transform) MatrixForParenting() (Matrix, Matrix) {
t.parentDirty = false t.parentDirty = false
} else { } else {
m = t.parentMatrix m = t.parentMatrice
mi = t.parentInverted mi = t.parentInverted
} }
if t.parent != nil { if t.parent != nil {
pm, pmi := t.parent.MatrixForParenting() pm, pmi := t.parent.GetMatriceForParenting()
m.Concat(pm) m.Concat(pm)
pmi.Concat(mi) pmi.Concat(mi)
mi = pmi mi = pmi
} }
return m, mi return m, mi
} }
// Returns the GeoM with corresponding // Returns the GeoM with corresponding
// to the transfrom transformation. // to the transfrom transformation.
func (t *Transform)Matrix() Matrix { func (t *Transform) GetMatrice() mx.Matrice {
var m, pm Matrix var m, pm mx.Matrice
// Calculating only if we changed the structure anyhow. // Calculating only if we changed the structure anyhow.
if t.dirty { if t.dirty {
@ -237,7 +242,7 @@ func (t *Transform)Matrix() Matrix {
} }
if t.parent != nil { if t.parent != nil {
pm, _ = t.parent.MatrixForParenting() pm, _ = t.parent.GetMatriceForParenting()
m.Concat(pm) m.Concat(pm)
} }

20
ox/tri.go Normal file
View file

@ -0,0 +1,20 @@
package ox
type DrawableTriangles struct {
Triangles
Colority
}
func (r *DrawableTriangles) MakeEVertices(c *Context) []EVertex {
m := c.Camera.RealMatrix()
vs := make([]ebiten.Vertex, len(r.Triangles) * 3)
var buf Vertex
buf.Color = r.Color
for i := range r.Triangles {
for j := range r.Triangles[i] {
buf.Dst = r.Triangles[i][j].Apply(m)
vs[i*3 + j] = buf.Ebiten()
}
}
return vs
}

View file

@ -1,8 +0,0 @@
package gg
type Point = Vector
type Points []Point
func (pts Points) Empty() bool {
return len(pts) == 0
}

View file

@ -5,13 +5,14 @@ import (
//"fmt" //"fmt"
) )
type Shader = ebiten.Shader
type ShaderOptions struct { type ShaderOptions struct {
Shader *Shader Shader *Shader
Uniforms map[string] any Uniforms map[string] any
Images [4]*Image Images [4]*Image
} }
type Shader = ebiten.Shader
var ( var (
// The shader is for example only. // The shader is for example only.
SolidWhiteColorShader = MustNewShader([]byte(` SolidWhiteColorShader = MustNewShader([]byte(`

1
short.go Normal file
View file

@ -0,0 +1 @@
package gg

52
text.go
View file

@ -4,8 +4,8 @@ import (
//"strings" //"strings"
"golang.org/x/image/font" "golang.org/x/image/font"
"golang.org/x/image/font/opentype" "golang.org/x/image/font/opentype"
"github.com/hajimehoshi/ebiten/v2/text" //"github.com/hajimehoshi/ebiten/v2/text"
"github.com/hajimehoshi/ebiten/v2" //"github.com/hajimehoshi/ebiten/v2"
"io" "io"
//"fmt" //"fmt"
) )
@ -14,20 +14,22 @@ import (
var ( var (
FontHintingNone = font.HintingNone FontHintingNone = font.HintingNone
) )
type Face = font.Face type Face = font.Face
type TtfFont = opentype.Font type FontTTF = opentype.Font
type TtfFaceOptions = opentype.FaceOptions type FaceOptionsTTF = opentype.FaceOptions
type TtfFace = opentype.Face type FaceTTF = opentype.Face
func MakeFaceFromTtf(src io.ReaderAt, opts *TtfFaceOptions) (Face, error) { func MakeFaceFromTTF(
fnt, err := ParseTtfFont(src) src io.ReaderAt,
opts *FaceOptionsTTF,
) (Face, error) {
fnt, err := ParseFontTTF(src)
if err != nil { if err != nil {
return nil, err return nil, err
} }
face, err := NewTtfFace(fnt, opts) face, err := NewFaceTTF(fnt, opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -35,35 +37,17 @@ func MakeFaceFromTtf(src io.ReaderAt, opts *TtfFaceOptions) (Face, error) {
return face, nil return face, nil
} }
func ParseTtfFont(src io.ReaderAt) (*TtfFont, error) { func ParseFontTTF(
src io.ReaderAt,
) (*FontTTF, error) {
return opentype.ParseReaderAt(src) return opentype.ParseReaderAt(src)
} }
func NewTtfFace(fnt *TtfFont, opts *TtfFaceOptions) (Face, error) { func NewFaceTTF(
fnt *FontTTF,
opts *FaceOptionsTTF,
) (Face, error) {
return opentype.NewFace(fnt, opts) return opentype.NewFace(fnt, opts)
} }
// The type implements basic drawable text.
// (Now needs to implement rotation, scaling etc, cause now only position)
type Text struct {
Object
Transform
Data string
Face Face
Colority
Visibility
}
func (txt *Text) Draw(c *Context) []EVertex {
m := txt.Matrix()
m.Concat(c.Camera.RealMatrix())
//x, y := txt.Position.XY()
//text.Draw(c.Image)
text.DrawWithOptions(c.Image, txt.Data, txt.Face,
&ebiten.DrawImageOptions{
GeoM: m,
},
)
return nil
}

View file

@ -1,144 +0,0 @@
package gg
import (
"math"
"github.com/hajimehoshi/ebiten/v2"
)
type EVertex = ebiten.Vertex
// Ebitens vector in better abstractions like Vectors.
type Vertex struct {
Dst Vector
Src Vector
Colority
}
type Triangle [3]Vector
type DrawableTriangles struct {
Triangles
Colority
}
func (v Vertex) Ebiten() EVertex {
return EVertex {
DstX: float32(v.Dst.X),
DstY: float32(v.Dst.Y),
SrcX: float32(v.Src.X),
SrcY: float32(v.Src.Y),
ColorR: float32(v.Color.R)/(float32(MaxColorV)),
ColorG: float32(v.Color.G)/(float32(MaxColorV)),
ColorB: float32(v.Color.B)/(float32(MaxColorV)),
ColorA: float32(v.Color.A)/(float32(MaxColorV)),
}
}
// Returns the area of the triangle.
func (t Triangle) Area() Float {
x1 := t[0].X
y1 := t[0].Y
x2 := t[1].X
y2 := t[1].Y
x3 := t[2].X
y3 := t[2].Y
return math.Abs( (x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2))/2)
}
func sqr(v Float) Float {
return v * v
}
// Return squares of lengths of sides of the triangle.
func (t Triangle) SideLengthSquares() ([3]Float) {
l1 := Edge{t[0], t[1]}.LenSqr()
l2 := Edge{t[1], t[2]}.LenSqr()
l3 := Edge{t[2], t[0]}.LenSqr()
return [3]Float{l1, l2, l3}
}
// Check whether the point is in the triangle.
func (t Triangle) ContainsPoint(p Point) bool {
d1 := Triangle{p, t[0], t[1]}.Sgn()
d2 := Triangle{p, t[1], t[2]}.Sgn()
d3 := Triangle{p, t[2], t[0]}.Sgn()
neg := (d1 < 0) || (d2 < 0) || (d3 < 0)
pos := (d1 > 0) || (d2 > 0) || (d3 > 0)
return !(neg && pos)
}
func (t Triangle) ContainedPoints(pts Points) []Point {
ret := []Point{}
for i := range pts {
if t.ContainsPoint(pts[i]) {
ret = append(ret, pts[i])
}
}
return ret
}
func (t Triangle) Sgn() Float {
return (t[0].X - t[2].X) * (t[1].Y - t[2].Y) -
(t[1].X - t[2].X) * (t[0].Y - t[2].Y)
}
func (t Triangle) Edges() Edges {
return Edges{
Edge{t[0], t[1]},
Edge{t[1], t[2]},
Edge{t[2], t[0]},
}
}
func (t Triangle) Vertices() Vertices {
return Vertices{
t[0], t[1], t[2],
}
}
type Triangles []Triangle
func (ts Triangles) ContainedPoints(pts Points) (Points) {
ret := []Point{}
for _, t := range ts {
ps := t.ContainedPoints(pts)
ret = append(ret, ps...)
}
return ret
}
func (ts Triangles) Vertices() Vertices {
ret := make(Vertices, 0, len(ts)*3)
for _, t := range ts {
ret = append(ret, t.Vertices()...)
}
return ret
}
func (ts Triangles) Edges() Edges {
ret := make(Edges, 0, len(ts)*3)
for _, t := range ts {
ret = append(ret, t.Edges()...)
}
return ret
}
func (r *DrawableTriangles) MakeEVertices(c *Context) []EVertex {
m := c.Camera.RealMatrix()
vs := make([]ebiten.Vertex, len(r.Triangles) * 3)
var buf Vertex
buf.Color = r.Color
for i := range r.Triangles {
for j := range r.Triangles[i] {
buf.Dst = r.Triangles[i][j].Apply(m)
vs[i*3 + j] = buf.Ebiten()
}
}
return vs
}

View file

@ -1,5 +1,26 @@
package gg package gg
type Vertice = Point import "github.com/hajimehoshi/ebiten/v2"
type Vertices = []Vertice import "surdeus.su/core/gg/mx"
type EVertice = ebiten.Vertex
// Ebitens vector in better abstractions like Vectors.
type Vertice struct {
Dst mx.Vector
Src mx.Vector
Color
}
func (v Vertice) ToAPI() EVertice {
return EVertice {
DstX: float32(v.Dst.X),
DstY: float32(v.Dst.Y),
SrcX: float32(v.Src.X),
SrcY: float32(v.Src.Y),
ColorR: float32(v.Color.R)/(float32(MaxColorValue)),
ColorG: float32(v.Color.G)/(float32(MaxColorValue)),
ColorB: float32(v.Color.B)/(float32(MaxColorValue)),
ColorA: float32(v.Color.A)/(float32(MaxColorValue)),
}
}