Compare commits
31 commits
Author | SHA1 | Date | |
---|---|---|---|
ab8a3f3387 | |||
eda12973e9 | |||
7c6f8f8f45 | |||
c1a2d6b741 | |||
0abd485f55 | |||
8a57cfe9e6 | |||
5cd9a65dfb | |||
cde993db48 | |||
234f4f4d0c | |||
e697383867 | |||
e78a0d9881 | |||
86ae2fbde9 | |||
88c1aa9577 | |||
ca0e87b4d1 | |||
9203bd1e79 | |||
b7d7ab9bf9 | |||
b479bf1988 | |||
82da88509a | |||
7b9025b062 | |||
8fcb1c2dc5 | |||
31940fa899 | |||
f910d4f99b | |||
7dcf0eeca8 | |||
6994dff47b | |||
f2d7feb8a4 | |||
60cd4e9e75 | |||
ab406b1249 | |||
733c10f6fd | |||
0f6732bcf9 | |||
08c950a669 | |||
a35bf9b27a |
69 changed files with 3080 additions and 1528 deletions
51
ax/ani.go
Normal file
51
ax/ani.go
Normal 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
52
ax/define.go
Normal 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
|
||||
}
|
3
bg.go
Normal file
3
bg.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
package gg
|
||||
|
||||
|
3
btest.sh
Executable file
3
btest.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
go build -o ./exe/ ./cmd/test/
|
65
camera.go
65
camera.go
|
@ -1,52 +1,21 @@
|
|||
package gg
|
||||
|
||||
// Implements the camera component
|
||||
// for the main window.
|
||||
type Camera struct {
|
||||
// The shaders that will be applied to everything
|
||||
// that the camera shows.
|
||||
ShaderOptions
|
||||
Transform
|
||||
buffered bool
|
||||
buf Matrix
|
||||
engine *Engine
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
// The type describes what
|
||||
// a window camera object must implement.
|
||||
type Camera interface {
|
||||
// Get the matrice to apply
|
||||
// camera's features.
|
||||
GetRealMatrice(Context) mx.Matrice
|
||||
|
||||
// The way to convert from real to absolute.
|
||||
GetAbsMatrice(Context) mx.Matrice
|
||||
|
||||
// The shaders to apply on
|
||||
// everything on the camera.
|
||||
GetShaderOptions() *ShaderOptions
|
||||
|
||||
GetAbsWinSize(Context) mx.Vector
|
||||
}
|
||||
|
||||
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{}
|
||||
g.Translate(-c.Position.X, -c.Position.Y)
|
||||
g.Rotate(c.Rotation)
|
||||
siz := c.engine.AbsWinSize()
|
||||
g.Translate(c.Around.X * siz.X, c.Around.Y * siz.Y)
|
||||
g.Scale(c.Scale.X, c.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
|
||||
}
|
||||
|
|
40
cmd/math/main.go
Normal file
40
cmd/math/main.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"surdeus.su/core/gg"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Float = gg.Float
|
||||
|
||||
func EdgesFromVals(vals ...Float) gg.Edges {
|
||||
ret := gg.Edges{}
|
||||
for i := 0; i < len(vals); i += 4 {
|
||||
ret = append(
|
||||
ret,
|
||||
gg.Edge{
|
||||
gg.Point{
|
||||
vals[i], vals[i+1],
|
||||
},
|
||||
gg.Point{
|
||||
vals[i+2], vals[i+3],
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func main() {
|
||||
e1 := EdgesFromVals(
|
||||
0, 0, 5, 5,
|
||||
)
|
||||
e2 := EdgesFromVals(
|
||||
0, 1, 5, 0,
|
||||
0, 0, 5, 5,
|
||||
)
|
||||
val, ok := e1.CrossWithEdges(e2)
|
||||
bts, _ := json.MarshalIndent(val, "", "\t")
|
||||
fmt.Println(string(bts), ok)
|
||||
}
|
81
cmd/test/camera.go
Normal file
81
cmd/test/camera.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package main
|
||||
|
||||
import "surdeus.su/core/gg/ox"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
import "surdeus.su/core/gg"
|
||||
|
||||
import "log"
|
||||
|
||||
var (
|
||||
camera *Camera
|
||||
)
|
||||
|
||||
type Camera struct {
|
||||
ox.Camera
|
||||
ScaleSpeed mx.Float
|
||||
RotationSpeed mx.Float
|
||||
}
|
||||
|
||||
func NewCamera() *Camera {
|
||||
camera := &Camera{}
|
||||
camera.Camera = ox.NewCamera()
|
||||
camera.ScaleSpeed = 5.
|
||||
camera.RotationSpeed = .3
|
||||
return camera
|
||||
}
|
||||
|
||||
func (cam *Camera) OnUpdate(c gg.Context) {
|
||||
dt := c.Engine().DT().Seconds()
|
||||
mov := c.Events().Mouse.Move
|
||||
wheel := c.Events().Mouse.Wheel
|
||||
shiftPressed := c.Engine().IsPressed(gg.KeyShift)
|
||||
d := float64(1.)
|
||||
if shiftPressed {
|
||||
d *= -1
|
||||
}
|
||||
|
||||
// Moving.
|
||||
if mov != nil && c.Engine().IsButtoned(
|
||||
gg.MouseButtonRight,
|
||||
) {
|
||||
cam.Move(mov.AbsDelta)
|
||||
}
|
||||
|
||||
for _, btn := range c.Events().Mouse.Downs {
|
||||
switch btn.MouseButton {
|
||||
case gg.MouseButtonLeft :
|
||||
t := NewTri(ox.T().SetPosition(
|
||||
c.Engine().GetAbsCursorPosition(),
|
||||
))
|
||||
t.Spawned = true
|
||||
c.Engine().Spawn(t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Scale.
|
||||
if wheel != nil {
|
||||
k := 1.5
|
||||
log.Println(wheel.Offset.Y)
|
||||
if wheel.Offset.Y < 0 {
|
||||
k = 1/k
|
||||
}
|
||||
|
||||
cam.Scale(mx.V2(
|
||||
k,
|
||||
//wheel.Offset.Y * dt * cam.ScaleSpeed * 40,
|
||||
))
|
||||
}
|
||||
|
||||
// Scale and rotation.
|
||||
for _, key := range c.Engine().GetKeyboardKeys() {
|
||||
switch key {
|
||||
case gg.KeyF:
|
||||
cam.AddScale(mx.VX(d*cam.ScaleSpeed * dt))
|
||||
case gg.KeyG:
|
||||
cam.AddScale(mx.VY(d*cam.ScaleSpeed * dt))
|
||||
case gg.KeyR:
|
||||
cam.Rotate(d*mx.Pi * cam.RotationSpeed * dt)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,80 +1,78 @@
|
|||
package main
|
||||
|
||||
import "vultras.su/core/gg"
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/ox"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"fmt"
|
||||
//"strings"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type Debug struct {
|
||||
gg.Object
|
||||
ox.ObjectImpl
|
||||
ox.Visibility
|
||||
}
|
||||
|
||||
func (d *Debug) Draw(c *Context) {
|
||||
e := c.Engine
|
||||
keyStrs := []string{}
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"counter: %d", counter,
|
||||
))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"tps: %d", int(c.TPS()),
|
||||
))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"fps: %d", int(c.FPS()),
|
||||
))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"relPlayerPos: %v", player.Position,
|
||||
))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"absPlayerPos: %v", player.AbsPosition(),
|
||||
))
|
||||
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"relTriPos: %v", tri.Position,
|
||||
))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"absTriPos: %v", tri.AbsPosition(),
|
||||
))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"absTriRot: %v", gg.Degree(tri.AbsRotation()),
|
||||
))
|
||||
|
||||
keys := []string{}
|
||||
for _, k := range e.Keys() {
|
||||
keys = append(keys, k.String())
|
||||
func (d *Debug) OnUpdate(c Context) {
|
||||
for _, key := range c.Events().Keyboard.Downs {
|
||||
switch key.Key {
|
||||
case gg.KeyF11:
|
||||
d.ToggleVisibility()
|
||||
//d.Visible = !d.IsVisible()
|
||||
}
|
||||
}
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"keys: %s", strings.Join(keys, ", "),
|
||||
))
|
||||
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"buttons: %v", c.MouseButtons(),
|
||||
))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"wheel: %v", c.Wheel(),
|
||||
))
|
||||
/*if rectMove.ContainsPoint(e.AbsCursorPosition()) {
|
||||
keyStrs = append(keyStrs, "contains cursor")
|
||||
}
|
||||
|
||||
if rectMove.Vertices().Contained(rect).Len() > 0 ||
|
||||
rect.Vertices().Contained(rectMove).Len() > 0 {
|
||||
keyStrs = append(keyStrs, "rectangles intersect")
|
||||
}*/
|
||||
|
||||
keyStrs = append(keyStrs, fmt.Sprintf(
|
||||
"camera position: %v %v",
|
||||
c.Camera.Position.X,
|
||||
c.Camera.Position.Y,
|
||||
))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf("realCursorPos: %v", e.CursorPosition()))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf("absCursorPos: %v", e.AbsCursorPosition()))
|
||||
keyStrs = append(keyStrs, fmt.Sprintf("absWinSize: %v", c.AbsWinSize()))
|
||||
|
||||
e.DebugPrint(c.Image,
|
||||
strings.Join(keyStrs, "\n"))
|
||||
|
||||
}
|
||||
|
||||
func (d *Debug) IsVisible() bool { return true }
|
||||
func (d *Debug) OnStart(c Context) {
|
||||
d.Visible = true
|
||||
}
|
||||
|
||||
func (d *Debug) GetLayer() gg.Layer {return LayerHighest}
|
||||
|
||||
func (d *Debug) Draw(c Context) *gg.Drawing {
|
||||
e := c.Engine()
|
||||
relTri := tri.Rel()
|
||||
relPlayer := player.Rel()
|
||||
keys := c.Engine().GetKeyboardKeys()
|
||||
|
||||
c.Dprint(
|
||||
"counter = ", counter, "\n",
|
||||
"tps = ", int(c.Engine().TPS()), "\n",
|
||||
"fps = ", int(c.Engine().FPS()), "\n",
|
||||
"dframe = ", int(c.Engine().Dframe()), "\n",
|
||||
"frame = ", int(c.Engine().Frame()), "\n",
|
||||
"relPlayerPos = ", relPlayer.GetPosition(), "\n",
|
||||
"absPlayerPos = ", player.GetPosition(), "\n",
|
||||
"relTriPos = ", relTri.GetPosition(), "\n",
|
||||
"absTriPos = ", tri.GetPosition(), "\n",
|
||||
"absTriRot = " , mx.Degree(tri.GetRotation()), "\n",
|
||||
"keys = ", keys, "\n",
|
||||
|
||||
"GetMouseButtons(...) = ",
|
||||
c.Engine().GetMouseButtons(), "\n",
|
||||
|
||||
"GetMouseWheel(...) = ", "\n",
|
||||
c.Engine().GetMouseWheel(), "\n",
|
||||
|
||||
"camera.GetPosition(...) = ",
|
||||
camera.GetPosition().X,
|
||||
camera.GetPosition().Y, "\n",
|
||||
|
||||
"GetRealCursorPosition(...) = ",
|
||||
e.GetRealCursorPosition(), "\n",
|
||||
|
||||
"GetAbsCursorPos(...) = ",
|
||||
e.GetAbsCursorPosition(), "\n",
|
||||
|
||||
"GetAbsWinSize(...) = ",
|
||||
c.Engine().GetAbsWinSize(), "\n",
|
||||
|
||||
"ConnectedTriangleAmount = ",
|
||||
len(c.Engine().Objects().FindByTags(
|
||||
TagTriangle, TagConnectedToPlayer,
|
||||
)), "\n",
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
52
cmd/test/grid.go
Normal file
52
cmd/test/grid.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package main
|
||||
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/ox"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
type Grid struct {
|
||||
ox.Sprite
|
||||
Cage, Width mx.Float
|
||||
}
|
||||
|
||||
func (grid *Grid) OnUpdate(c Context) {
|
||||
//d := c.IsPressed(gg.KeyShift)
|
||||
for _, down := range c.Events().Keyboard.Downs {
|
||||
switch down.Key {
|
||||
case gg.Key9:
|
||||
grid.Cage /= 10
|
||||
grid.Width /= 5
|
||||
case gg.Key0:
|
||||
grid.Cage *= 10
|
||||
grid.Width *= 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewGrid() *Grid {
|
||||
grid := &Grid{}
|
||||
grid.Transform = *ox.T()
|
||||
grid.SetAround(mx.V2(0))
|
||||
grid.Layer = LayerBackground
|
||||
grid.Images[0] = gg.NewImage(720, 480)
|
||||
grid.Images[0].Fill(gg.RGBA(1, 1, 1, 1))
|
||||
grid.Shader = bgShader
|
||||
grid.Uniforms = map[string]any{}
|
||||
//bg.Floating = true
|
||||
grid.Visible = true
|
||||
grid.Floating = true
|
||||
grid.Cage = 100
|
||||
grid.Width = 5
|
||||
return grid
|
||||
}
|
||||
|
||||
func (grid *Grid) Draw(c Context) *gg.Drawing {
|
||||
scale := camera.GetScale()
|
||||
pos := camera.GetPosition()
|
||||
grid.Uniforms["Zoom"] = mx.Float(scale.X)
|
||||
grid.Uniforms["PosX"] = pos.X
|
||||
grid.Uniforms["PosY"] = pos.Y
|
||||
grid.Uniforms["Cage"] = grid.Cage
|
||||
grid.Uniforms["Width"] = grid.Width
|
||||
return grid.Sprite.Draw(c)
|
||||
}
|
133
cmd/test/main.go
133
cmd/test/main.go
|
@ -1,42 +1,91 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"vultras.su/core/gg"
|
||||
"surdeus.su/core/gg"
|
||||
"surdeus.su/core/gg/ox"
|
||||
//"surdeus.su/core/gg/mx"
|
||||
"surdeus.su/core/gg/ax"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
|
||||
_ "github.com/silbinarywolf/preferdiscretegpu"
|
||||
//_ "github.com/silbinarywolf/preferdiscretegpu"
|
||||
"bytes"
|
||||
"log"
|
||||
//"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Context = gg.Context
|
||||
|
||||
const (
|
||||
HighestL gg.Layer = -iota
|
||||
DebugL
|
||||
TriangleL
|
||||
PlayerL
|
||||
RectL
|
||||
LowestL
|
||||
LayerHighest gg.Layer = -iota
|
||||
|
||||
LayerDebug
|
||||
LayerTriangle
|
||||
LayerPlayer
|
||||
LayerRect
|
||||
LayerBackground
|
||||
|
||||
LayerLowest
|
||||
)
|
||||
|
||||
const (
|
||||
TagPlayer gg.Tag = gg.TagLast + 1 + iota
|
||||
TagConnectedToPlayer
|
||||
TagTriangle
|
||||
)
|
||||
|
||||
const (
|
||||
Stand ax.AnimationID = iota
|
||||
Walk
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
playerImg *gg.Image
|
||||
player *Player
|
||||
rectMove gg.Rectangle
|
||||
rect *Rect
|
||||
tri *Tri
|
||||
playerAnimations ax.AnimationSet
|
||||
playerImg *gg.Image
|
||||
player *Player
|
||||
rectMove ox.Rectangle
|
||||
rect *Rect
|
||||
tri *Tri
|
||||
bgShader = gg.MustNewShader([]byte(`
|
||||
package main
|
||||
|
||||
//kage:unit pixels
|
||||
|
||||
//var cameraScaleX, cameraScaleY
|
||||
var Zoom float
|
||||
var PosX, PosY float
|
||||
var Cage float
|
||||
var Width float
|
||||
|
||||
const (
|
||||
)
|
||||
|
||||
func Fragment(dst vec4, src vec2, _ vec4) vec4 {
|
||||
c := imageSrc0UnsafeAt(src)
|
||||
dstSize := imageDstSize()/Zoom
|
||||
absDst := vec2(
|
||||
dst.x/Zoom - dstSize.x/2 - PosX,
|
||||
dst.y/Zoom - dstSize.y/2 - PosY,
|
||||
)
|
||||
cx := c*step(Cage-Width, mod(absDst.x-Width/2, Cage))
|
||||
cy := c*step(Cage-Width, mod(absDst.y-Width/2, Cage))
|
||||
if cx.a == 0 {
|
||||
return cy
|
||||
}
|
||||
return cx
|
||||
}
|
||||
`))
|
||||
)
|
||||
|
||||
func main() {
|
||||
e := gg.NewEngine(&gg.WindowConfig{
|
||||
Title: "Test title",
|
||||
Width: 720,
|
||||
Height: 480,
|
||||
VSync: true,
|
||||
Fullscreen: true,
|
||||
e := gg.NewEngine(gg.WindowConfig{
|
||||
Title: "Test title",
|
||||
Width: 720,
|
||||
Height: 480,
|
||||
VSync: true,
|
||||
Fullscreen: false,
|
||||
})
|
||||
camera = NewCamera()
|
||||
e.SetCamera(camera)
|
||||
|
||||
var err error
|
||||
playerImg, err = gg.LoadImage(bytes.NewReader(images.Runner_png))
|
||||
|
@ -45,15 +94,47 @@ func main() {
|
|||
}
|
||||
|
||||
rect = NewRect()
|
||||
|
||||
playerAnimations, err = ax.AnimationSetFromImage(
|
||||
playerImg,
|
||||
8, 3,
|
||||
10, 1,
|
||||
ax.AD(Stand).DefRow(0, 0, 1, 2, 3, 4),
|
||||
ax.AD(Walk).DefRow(1, 0, 1, 2, 3, 4, 5, 6, 7),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
player = NewPlayer()
|
||||
tri = NewTri()
|
||||
tri = NewTri(ox.T())
|
||||
|
||||
e.Spawn(&Debug{})
|
||||
e.Spawn(player)
|
||||
e.Spawn(rect)
|
||||
e.Spawn(tri)
|
||||
fmt.Println(rect.GetLayer(), player.GetLayer())
|
||||
|
||||
/*circle := &gg.Circle{}
|
||||
circle.Transform = gg.T()
|
||||
circle.Rotation = 300
|
||||
circle.Color = gg.Rgba(1, 1, 1, 1)
|
||||
circle.Antialias = true
|
||||
circle.Layer = HighestL
|
||||
circle.Visible = true*/
|
||||
//e.Spawn(circle)
|
||||
|
||||
text, err := NewText()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e.Spawn(text)
|
||||
|
||||
//fmt.Println(rect.GetLayer(), player.GetLayer())
|
||||
fmt.Println("Starting...")
|
||||
objects := []gg.Object{
|
||||
camera, &Debug{},
|
||||
|
||||
player, rect, tri,
|
||||
NewGrid(), text,
|
||||
}
|
||||
for _, object := range objects {
|
||||
e.Spawn(object)
|
||||
}
|
||||
err = e.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -1,137 +1,164 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
//"math/rand"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
import "vultras.su/core/gg"
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/ox"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
import "time"
|
||||
//import "log"
|
||||
|
||||
type Player struct {
|
||||
gg.Sprite
|
||||
MoveSpeed gg.Float
|
||||
ScaleSpeed gg.Float
|
||||
ox.AnimatedSprite
|
||||
MoveSpeed mx.Float
|
||||
ScaleSpeed mx.Float
|
||||
Spawned bool
|
||||
}
|
||||
|
||||
func NewPlayer() *Player {
|
||||
ret := &Player{}
|
||||
ret.Transform = gg.T()
|
||||
fmt.Println("transform:", ret.Transform)
|
||||
//ret.Parent = rect
|
||||
ret.Scale = gg.V2(1)
|
||||
// Around center.
|
||||
ret.Around = gg.V2(.5)
|
||||
ret.MoveSpeed = 90.
|
||||
//ret.Transform = ox.T()
|
||||
ret.AnimatedSprite = ox.NewAnimatedSprite(
|
||||
playerAnimations,
|
||||
time.Second / 10,
|
||||
)
|
||||
|
||||
|
||||
ret.SetScale(mx.V2(1))
|
||||
ret.SetAround(mx.V2(.5))
|
||||
|
||||
ret.MoveSpeed = 40.
|
||||
ret.ScaleSpeed = .2
|
||||
ret.Shader = gg.SolidWhiteColorShader
|
||||
|
||||
ret.Visible = true
|
||||
ret.Layer = LayerPlayer
|
||||
|
||||
ret.Images[0] = playerImg
|
||||
ret.Layer = PlayerL
|
||||
//ret.Collidable = true
|
||||
//ret.Resolvable = true
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *Player) Start(c *Context) {
|
||||
func (p *Player) OnStart(c Context) {
|
||||
p.Connect(rect)
|
||||
}
|
||||
|
||||
// Custom drawing function.
|
||||
func (p *Player) Draw(c *Context) {
|
||||
p.Sprite.Draw(c)
|
||||
t := p.Transform
|
||||
t.Scale.X *= 4.
|
||||
t.Scale.Y *= 4.
|
||||
|
||||
r := &gg.DrawableRectangle{}
|
||||
r.Color = gg.Rgba(0, 0, 1, 1)
|
||||
r.Rectangle = gg.Rectangle{
|
||||
Transform: t,
|
||||
func (p *Player) Draw(c Context) *gg.Drawing {
|
||||
return p.AnimatedSprite.Draw(c)
|
||||
prect := &ox.DrawableRectangle{
|
||||
Rectangle: p.GetRectangle(),
|
||||
}
|
||||
r.Draw(c)
|
||||
prect.Color = gg.RGBA(1, 1, 1, 1)
|
||||
return prect.Draw(c)
|
||||
}
|
||||
|
||||
func (p *Player) Update(c *Context) {
|
||||
dt := c.DT()
|
||||
cam := c.Camera
|
||||
keys := c.Keys()
|
||||
func (p *Player) OnUpdate(c Context) {
|
||||
//log.Println("OnUpdate(...)")
|
||||
if p.Spawned {
|
||||
return
|
||||
}
|
||||
e := c.Engine()
|
||||
dt := e.DT().Seconds()
|
||||
//cam := c.Camera
|
||||
keys := e.GetKeyboardKeys()
|
||||
/*r := gg.Rectangle{
|
||||
Transform: gg.T(),
|
||||
}
|
||||
r.Width = 150
|
||||
r.Height = 150
|
||||
col, _ := gg.Collide(p, r)
|
||||
fmt.Println(col.Crosses)*/
|
||||
//p.SetPosition(p.Position().Sub(gg.Y(2)))
|
||||
//p.Move(gg.Y(1))
|
||||
|
||||
walking := false
|
||||
shiftPressed := e.IsPressed(gg.KeyShift)
|
||||
d := float64(1)
|
||||
if shiftPressed {
|
||||
d *= -1
|
||||
}
|
||||
|
||||
shift := c.IsPressed(gg.KeyShift)
|
||||
//p.Uniforms["Random"] = any(rand.Float32())
|
||||
for _, v := range keys {
|
||||
switch v {
|
||||
for _, key := range keys {
|
||||
switch key {
|
||||
case gg.KeyQ:
|
||||
p.Scale = p.Scale.Add(gg.V(p.ScaleSpeed*dt, 0))
|
||||
case gg.KeyArrowUp:
|
||||
cam.Position.Y += p.MoveSpeed * dt
|
||||
case gg.KeyArrowLeft:
|
||||
cam.Position.X -= p.MoveSpeed * dt
|
||||
case gg.KeyArrowDown:
|
||||
cam.Position.Y -= p.MoveSpeed * dt
|
||||
case gg.KeyArrowRight:
|
||||
cam.Position.X += p.MoveSpeed * dt
|
||||
p.AddScale(mx.VX(d*p.ScaleSpeed * dt))
|
||||
case gg.KeyW:
|
||||
p.Position.Y += p.MoveSpeed * dt
|
||||
p.Move(mx.VY(-p.MoveSpeed * dt))
|
||||
walking = true
|
||||
p.Animate(Walk)
|
||||
case gg.KeyA:
|
||||
p.Position.X -= p.MoveSpeed * dt
|
||||
p.Move(mx.VX(-p.MoveSpeed * dt))
|
||||
|
||||
p.SetScale(mx.V(-1, 1))
|
||||
walking = true
|
||||
p.Animate(Walk)
|
||||
case gg.KeyS:
|
||||
p.Position.Y -= p.MoveSpeed * dt
|
||||
p.Move(mx.VY(p.MoveSpeed * dt))
|
||||
//p.Position.Y -= p.MoveSpeed * dt
|
||||
walking = true
|
||||
p.Animate(Walk)
|
||||
case gg.KeyD:
|
||||
p.Position.X += p.MoveSpeed * dt
|
||||
case gg.KeyR:
|
||||
cam.Rotation += gg.Pi * p.ScaleSpeed * dt
|
||||
case gg.KeyT:
|
||||
cam.Rotation -= gg.Pi * p.ScaleSpeed * dt
|
||||
case gg.KeyRightBracket:
|
||||
if shift {
|
||||
p.Rotation -= gg.Pi * 0.3 * dt
|
||||
} else {
|
||||
p.Rotation += gg.Pi * 0.3 * dt
|
||||
}
|
||||
case gg.KeyF:
|
||||
if shift {
|
||||
cam.Scale = cam.Scale.Add(gg.V2(p.ScaleSpeed * dt))
|
||||
} else {
|
||||
cam.Scale = cam.Scale.Add(gg.V2(-p.ScaleSpeed * dt))
|
||||
}
|
||||
case gg.KeyG:
|
||||
if shift {
|
||||
cam.Scale.Y -= gg.Pi * p.ScaleSpeed * dt
|
||||
} else {
|
||||
cam.Scale.Y += gg.Pi * p.ScaleSpeed * dt
|
||||
}
|
||||
case gg.KeyLeftBracket:
|
||||
if shift {
|
||||
rect.Rotation -= gg.Pi * 0.3 * dt
|
||||
} else {
|
||||
rect.Rotation += gg.Pi * 0.3 * dt
|
||||
}
|
||||
case gg.Key0:
|
||||
c.Del(p)
|
||||
case gg.KeyB:
|
||||
p.Move(mx.VX(p.MoveSpeed * dt))
|
||||
p.SetScale(mx.V(1, 1))
|
||||
walking = true
|
||||
p.Animate(Walk)
|
||||
|
||||
case gg.KeyE :
|
||||
p.Rotate(d*mx.Pi * 0.3 * dt)
|
||||
case gg.KeyX:
|
||||
c.Engine().Delete(p)
|
||||
case gg.KeyC:
|
||||
pp := *p
|
||||
counter++
|
||||
pp.Spawned = true
|
||||
//pp.Collidable = false
|
||||
//pp.Resolvable = false
|
||||
c.Engine().Spawn(&pp)
|
||||
}
|
||||
}
|
||||
|
||||
for _, event := range c.Events {
|
||||
switch ec := event.(type) {
|
||||
case *gg.KeyDown:
|
||||
if !walking {
|
||||
p.Animate(Stand)
|
||||
}
|
||||
|
||||
for _, down := range c.Events().Keyboard.Downs {
|
||||
switch {
|
||||
case ec.Key == gg.KeyB:
|
||||
if p.Layer != PlayerL {
|
||||
p.Layer = PlayerL
|
||||
case down.Key == gg.KeyZ:
|
||||
if p.Layer != LayerPlayer {
|
||||
p.Layer = LayerPlayer
|
||||
} else {
|
||||
p.Layer = HighestL
|
||||
p.Layer = LayerHighest
|
||||
}
|
||||
}
|
||||
case *gg.MouseMove:
|
||||
if !c.IsButtoned(gg.MouseButtonRight) {
|
||||
break
|
||||
}
|
||||
pos := c.Camera.Position
|
||||
c.Camera.Position = pos.Sub(ec.Abs)
|
||||
case *gg.WheelChange:
|
||||
c.Camera.Scale = c.Camera.Scale.Add(gg.V2(
|
||||
ec.Offset.Y * c.DT() * p.ScaleSpeed * 40,
|
||||
))
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*func (p *Player) GetCollisionInterest() []gg.CollisionType {
|
||||
return []gg.CollisionType{
|
||||
gg.CollisionStaticPhysics,
|
||||
}
|
||||
}*/
|
||||
|
||||
/*func (p *Player) Resolve(c *Context) {
|
||||
col := c.Collisions[0]
|
||||
if !p.Spawned && false {
|
||||
fmt.Printf(
|
||||
"frame[%d]: the col[0] len(%d): %T, %T, %d %d\n",
|
||||
c.Frame(), len(c.Collisions), col.What, col.With, len(col.Crosses), len(col.Points),
|
||||
)
|
||||
}
|
||||
for _, col := range c.Collisions {
|
||||
switch col.Type {
|
||||
case gg.CollisionStaticPhysics:
|
||||
LOOP:
|
||||
for {
|
||||
p.Move(gg.Y(-1))
|
||||
_, collides := gg.Collide(p, col.With)
|
||||
if !collides {
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
|
|
@ -1,30 +1,53 @@
|
|||
package main
|
||||
|
||||
import "vultras.su/core/gg"
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/ox"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
type Rect struct {
|
||||
gg.DrawableRectangle
|
||||
ox.DrawableRectangle
|
||||
}
|
||||
|
||||
func NewRect() *Rect {
|
||||
ret := &Rect{}
|
||||
ret.Scale = gg.V(200, 400)
|
||||
ret.Color = gg.Color{
|
||||
gg.MaxColorV,
|
||||
0,
|
||||
0,
|
||||
gg.MaxColorV,
|
||||
}
|
||||
ret.Layer = RectL
|
||||
ret.SetScale(mx.V(5, 5))
|
||||
ret.Color = gg.RGBA(1, 0, 0, 1)
|
||||
ret.Layer = LayerRect
|
||||
ret.Visible = true
|
||||
//ret.Collidable = true
|
||||
ret.Width = 100
|
||||
ret.Height = 200
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *Rect) Update(c *Context) {
|
||||
func (r *Rect) OnUpdate(c Context) {
|
||||
//r.R += 0.3 * e.DT()
|
||||
e := c.Engine()
|
||||
dt := e.DT().Seconds()
|
||||
shiftPressed := e.IsPressed(gg.KeyShift)
|
||||
d := float64(1)
|
||||
if shiftPressed {
|
||||
d *= -1
|
||||
}
|
||||
//r.Position = c.AbsCursorPosition()
|
||||
for _, v := range e.GetKeyboardKeys() {
|
||||
switch v {
|
||||
case gg.KeyArrowUp:
|
||||
r.Move(mx.VY(-10 * dt))
|
||||
case gg.KeyArrowDown:
|
||||
r.Move(mx.VY(10 * dt))
|
||||
case gg.KeyArrowLeft:
|
||||
r.Move(mx.VX(-10 * dt))
|
||||
case gg.KeyArrowRight:
|
||||
r.Move(mx.VX(10 * dt))
|
||||
case gg.KeyLeftBracket:
|
||||
r.Rotate(d*mx.Pi * 0.3 * dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Rect) Event(c *Context) {
|
||||
}
|
||||
/*func (r *Rect) CollisionType() gg.CollisionType {
|
||||
return gg.CollisionStaticPhysics
|
||||
}*/
|
||||
|
||||
|
|
41
cmd/test/text.go
Normal file
41
cmd/test/text.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"surdeus.su/core/gg"
|
||||
"surdeus.su/core/gg/ox"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
||||
"bytes"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type Text struct {
|
||||
ox.ObjectImpl
|
||||
ox.Text
|
||||
}
|
||||
|
||||
func NewText() (*Text, error) {
|
||||
txt := &Text{}
|
||||
txt.Transform = *ox.T()
|
||||
//txt.Position = gg.V2(400)
|
||||
txt.Color = gg.RGBA(1, 1, 1, 1)
|
||||
txt.Layer = LayerHighest
|
||||
txt.Visible = true
|
||||
txt.Data = "Hello, World!\nПривет, Мир!"
|
||||
var err error
|
||||
txt.Face, err = gg.MakeFaceFromTTF(
|
||||
bytes.NewReader(fonts.MPlus1pRegular_ttf),
|
||||
&gg.FaceOptionsTTF{
|
||||
Size: 32,
|
||||
DPI: 72,
|
||||
Hinting: gg.FontHintingNone,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txt, nil
|
||||
}
|
||||
|
||||
func (txt *Text) Update(c *gg.Context) {
|
||||
//txt.Data += string(c.Runes())
|
||||
}
|
|
@ -1,98 +1,102 @@
|
|||
package main
|
||||
|
||||
import "vultras.su/core/gg"
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/ox"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
//import "fmt"
|
||||
import "log"
|
||||
|
||||
var (
|
||||
counter int
|
||||
)
|
||||
|
||||
|
||||
type Tri struct {
|
||||
gg.DrawablePolygon
|
||||
ox.DrawablePolygon
|
||||
Spawned bool
|
||||
}
|
||||
|
||||
func NewTri() *Tri {
|
||||
func NewTri(t *ox.Transform) *Tri {
|
||||
ret := &Tri{}
|
||||
ret.Transform.Scale = gg.V2(1)
|
||||
|
||||
ret.Triangles = gg.Triangles{
|
||||
gg.Triangle{
|
||||
gg.V(0, 10),
|
||||
gg.V(100, 0),
|
||||
gg.V(0, -10),
|
||||
ret.Transform = *t
|
||||
ret.Triangles = mx.Triangles{
|
||||
mx.Triangle{
|
||||
mx.V(0, 10),
|
||||
mx.V(100, 0),
|
||||
mx.V(0, -10),
|
||||
},
|
||||
}
|
||||
ret.Color = gg.Rgba(1, 1, 0, 1)
|
||||
ret.Color = gg.RGBA(1, 1, 0, 1)
|
||||
ret.Visible = true
|
||||
ret.Layer = TriangleL
|
||||
ret.Connect(player)
|
||||
ret.Layer = LayerTriangle
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (t *Tri) Update(c *Context) {
|
||||
if t.Spawned {
|
||||
return
|
||||
func (t *Tri) GetTags(c Context) gg.TagMap {
|
||||
tags := gg.TagMap{
|
||||
TagTriangle: struct{}{},
|
||||
}
|
||||
dt := c.DT()
|
||||
if t.ContainsPoint(c.AbsCursorPosition()) {
|
||||
t.Color = gg.Rgba(0, 1, 0, 1)
|
||||
} else {
|
||||
t.Color = gg.Rgba(1, 0, 1, 1)
|
||||
if t.IsConnected() {
|
||||
tags[TagConnectedToPlayer] = struct{}{}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
keys := c.Keys()
|
||||
func (t *Tri) OnStart(c Context) {
|
||||
log.Println("added a tri")
|
||||
}
|
||||
|
||||
func (t *Tri) OnUpdate(c Context) {
|
||||
e := c.Engine()
|
||||
dt := e.DT().Seconds()
|
||||
|
||||
keys := e.GetKeyboardKeys()
|
||||
|
||||
d := +1.
|
||||
if c.IsPressed(gg.KeyShift) {
|
||||
if e.IsPressed(gg.KeyShift) {
|
||||
d = -1
|
||||
}
|
||||
|
||||
for _, key := range keys { switch key {
|
||||
case gg.KeyM:
|
||||
absPos := t.AbsPosition()
|
||||
t.SetAbsPosition(
|
||||
absPos.Add(gg.V(0, 100*c.DT()*d)),
|
||||
)
|
||||
case gg.KeyN:
|
||||
absPos := t.AbsPosition()
|
||||
t.SetAbsPosition(
|
||||
absPos.Add(gg.V(100*c.DT()*d, 0)),
|
||||
)
|
||||
case gg.KeyV:
|
||||
t.Rotation += d * gg.Pi * 0.3 * dt
|
||||
case gg.Key2 :
|
||||
if t.Spawned {
|
||||
break
|
||||
}
|
||||
t.Triangles = append(t.Triangles, gg.Triangle{
|
||||
gg.V(0, 10 + gg.Float(counter)),
|
||||
gg.V(100 + gg.Float(counter), 0),
|
||||
gg.V(0, -10 - gg.Float(counter)),
|
||||
})
|
||||
case gg.Key3 :
|
||||
if t.Spawned {
|
||||
break
|
||||
}
|
||||
tt := *t
|
||||
tt.Spawned = true
|
||||
tt.Visible = false
|
||||
tt.Disconnect()
|
||||
if c.Spawn(&tt) == nil {
|
||||
counter++
|
||||
}
|
||||
}}
|
||||
for _, key := range keys {
|
||||
switch key {
|
||||
|
||||
for _, event := range c.Events {
|
||||
switch e := event.(type) { case *gg.KeyDown:
|
||||
switch e.Key {
|
||||
case gg.Key1 :
|
||||
if t.Connected() {
|
||||
case gg.KeyM:
|
||||
t.Move(mx.VY(100 * dt * d))
|
||||
case gg.KeyN:
|
||||
t.Move(mx.VX(100 * dt * d))
|
||||
|
||||
case gg.KeyV:
|
||||
t.Rotate(d * mx.Pi * 0.3 * dt)
|
||||
case gg.Key2:
|
||||
if t.Spawned {
|
||||
break
|
||||
}
|
||||
case gg.Key3:
|
||||
if t.Spawned {
|
||||
break
|
||||
}
|
||||
tt := *t
|
||||
tt.Spawned = true
|
||||
tt.Disconnect()
|
||||
if e.Spawn(&tt) {
|
||||
counter++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, down := range c.Events().Keyboard.Downs {
|
||||
switch down.Key {
|
||||
case gg.Key1:
|
||||
if t.IsConnected() {
|
||||
t.Disconnect()
|
||||
} else {
|
||||
t.Connect(player)
|
||||
}
|
||||
}}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tri *Tri) Draw(c Context) *gg.Drawing {
|
||||
return tri.DrawablePolygon.Draw(c)
|
||||
}
|
||||
|
|
33
collider.go
33
collider.go
|
@ -1,33 +0,0 @@
|
|||
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
|
||||
}
|
||||
|
||||
// The structure represents all
|
||||
// information on collisions.
|
||||
type Collision struct {
|
||||
Current, With any
|
||||
}
|
||||
|
||||
type PointContainer interface {
|
||||
ContainsPoint(Point) bool
|
||||
}
|
||||
|
||||
// Implementing the interface lets the engine
|
||||
// to determine if the object collides with anything.
|
||||
// Mostly will use the Collide function with some
|
||||
// inner structure field as first argument.
|
||||
// The Collide method will be called on collisions.
|
||||
type Collider interface {
|
||||
Collides(Collider) *Collision
|
||||
Collide(*Collision)
|
||||
}
|
||||
|
||||
|
56
context.go
56
context.go
|
@ -1,17 +1,49 @@
|
|||
package gg
|
||||
|
||||
type contextType int
|
||||
const (
|
||||
startContext contextType = iota
|
||||
updateContext
|
||||
eventContext
|
||||
drawContext
|
||||
deleteContext
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
// The type is used to provide
|
||||
// custom behaviour for drawing and updating etc.
|
||||
type Context struct {
|
||||
typ contextType
|
||||
Events []any
|
||||
*Engine
|
||||
*Image
|
||||
events Events
|
||||
engine *Engine
|
||||
image *Image
|
||||
}
|
||||
|
||||
// Get the current engine.
|
||||
func (c Context) Engine() *Engine {
|
||||
return c.engine
|
||||
}
|
||||
|
||||
func (c Context) Camera() Camera {
|
||||
if c.engine == nil {
|
||||
return nil
|
||||
}
|
||||
return c.engine.camera
|
||||
}
|
||||
|
||||
// Get the image to draw to.
|
||||
// Can be accessed only in the
|
||||
// Draw(...) call.
|
||||
func (c Context) Image() *Image {
|
||||
return c.image
|
||||
}
|
||||
|
||||
// Get the events.
|
||||
// Available only in the Update(...) call.
|
||||
func (c Context) Events() Events {
|
||||
return c.events
|
||||
}
|
||||
|
||||
func (c Context) Dprint(v ...any) {
|
||||
c.Engine().DebugPrint(c.Image(), fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (c Context) Dprintln(v ...any) {
|
||||
c.Engine().DebugPrint(c.Image(), fmt.Sprintln(v...))
|
||||
}
|
||||
|
||||
func (c Context) Dprintf(format string, v ...any) {
|
||||
c.Engine().DebugPrint(c.Image(), fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
|
|
109
cx/collider.go
Normal file
109
cx/collider.go
Normal 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
|
||||
}
|
15
draw.go
Normal file
15
draw.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package gg
|
||||
|
||||
type Drawing struct {
|
||||
Vertices []Vertice
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
407
engine.go
407
engine.go
|
@ -3,27 +3,20 @@ package gg
|
|||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"github.com/di4f/gods/maps"
|
||||
//"fmt"
|
||||
"surdeus.su/core/gods/maps"
|
||||
"time"
|
||||
"slices"
|
||||
"sync"
|
||||
)
|
||||
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
const (
|
||||
LayerBufSize = 0
|
||||
MaxVertices = 1 << 16
|
||||
)
|
||||
|
||||
type GraphicsLibrary = ebiten.GraphicsLibrary
|
||||
type RunOptions = ebiten.RunGameOptions
|
||||
|
||||
// The type represents order of drawing.
|
||||
// Higher values are drawn later.
|
||||
type Layer float64
|
||||
|
||||
func (l Layer) GetLayer() Layer {
|
||||
return l
|
||||
}
|
||||
|
||||
// Window configuration type.
|
||||
type WindowConfig struct {
|
||||
|
@ -44,43 +37,51 @@ type WindowConfig struct {
|
|||
VSync bool
|
||||
}
|
||||
|
||||
type Objects struct {
|
||||
store []Objecter
|
||||
}
|
||||
|
||||
// The main structure that represents current state of [game] engine.
|
||||
// The main structure that represents
|
||||
// current state of [game] engine.
|
||||
type Engine struct {
|
||||
wcfg *WindowConfig
|
||||
wcfg WindowConfig
|
||||
|
||||
// The main holder for objects.
|
||||
// Uses the map structure to quickly
|
||||
// delete and create new objects.
|
||||
Objects *Objects
|
||||
objects *Objects
|
||||
|
||||
// The main camera to display in window.
|
||||
// If is set to nil then the engine will panic.
|
||||
Camera *Camera
|
||||
camera Camera
|
||||
|
||||
// The same delta time for all frames
|
||||
// and all objects.
|
||||
drawLastTime time.Time
|
||||
drawdt Duration
|
||||
|
||||
// Frame delta time.
|
||||
dt Duration
|
||||
lastTime time.Time
|
||||
dt Float
|
||||
|
||||
// Temporary stuff
|
||||
keys, prevKeys []Key
|
||||
buttons MouseButtonMap
|
||||
wheel Vector
|
||||
cursorPos Vector
|
||||
wheel mx.Vector
|
||||
cursorPos mx.Vector
|
||||
outerEvents, handleEvents EventChan
|
||||
wg sync.WaitGroup
|
||||
|
||||
bufs [LayerBufSize]*Image
|
||||
//bufs [LayerBufSize]*Image
|
||||
vertices map[Layer] []ebiten.Vertex
|
||||
//vindices []uint16
|
||||
|
||||
// Draw frame.
|
||||
dframe uint
|
||||
|
||||
// Frame.
|
||||
frame uint
|
||||
|
||||
runes []rune
|
||||
}
|
||||
|
||||
type engine Engine
|
||||
|
||||
// Get currently pressed keys.
|
||||
func (e *Engine) Keys() []Key {
|
||||
func (e *Engine) GetKeyboardKeys() []Key {
|
||||
return e.keys
|
||||
}
|
||||
|
||||
|
@ -89,7 +90,7 @@ func (e *Engine) GraphicsLibrary() GraphicsLibrary {
|
|||
}
|
||||
|
||||
// Returns currently pressed buttons.
|
||||
func (e *Engine) MouseButtons() []MouseButton {
|
||||
func (e *Engine) GetMouseButtons() []MouseButton {
|
||||
ret := make([]MouseButton, len(e.buttons))
|
||||
i := 0
|
||||
for v := range e.buttons {
|
||||
|
@ -102,75 +103,58 @@ func (e *Engine) MouseButtons() []MouseButton {
|
|||
|
||||
// Returns new empty Engine.
|
||||
func NewEngine(
|
||||
cfg *WindowConfig,
|
||||
cfg WindowConfig,
|
||||
) *Engine {
|
||||
/*w := Float(cfg.Width)
|
||||
h := Float(cfg.Height)*/
|
||||
|
||||
ret := &Engine{}
|
||||
|
||||
ret.wcfg = cfg
|
||||
ret.Camera = ret.NewCamera()
|
||||
ret.outerEvents = make(EventChan)
|
||||
ret.handleEvents = make(EventChan)
|
||||
ret.Objects = &Objects{}
|
||||
ret.objects = NewObjects()
|
||||
ret.buttons = MouseButtonMap{}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Get the real window size in the current context.
|
||||
func (c *Engine) RealWinSize() Vector {
|
||||
var w, h int
|
||||
if c.wcfg.Fullscreen {
|
||||
w, h = ebiten.ScreenSizeInFullscreen()
|
||||
} else {
|
||||
w, h = c.wcfg.Width, c.wcfg.Height
|
||||
}
|
||||
return V(
|
||||
Float(w),
|
||||
Float(h),
|
||||
)
|
||||
func (e *Engine) Camera() Camera {
|
||||
return e.camera
|
||||
}
|
||||
|
||||
func (c *Engine) AbsWinSize() Vector {
|
||||
return c.RealWinSize().Div(c.Camera.Scale)
|
||||
func (e *Engine) SetCamera(c Camera) *Engine {
|
||||
e.camera = c
|
||||
return e
|
||||
}
|
||||
|
||||
|
||||
func (e *Engine) EventInput() EventChan {
|
||||
return e.outerEvents
|
||||
}
|
||||
|
||||
// Add new object considering what
|
||||
// interfaces it implements.
|
||||
func (e *Engine) Spawn(b Objecter) error {
|
||||
/*if e.Objects.Has(b) {
|
||||
return ObjectExistErr
|
||||
}*/
|
||||
func (e *Engine) Exist(object Object) bool {
|
||||
return e.objects.has(object)
|
||||
}
|
||||
|
||||
b.Start(&Context{Engine: e})
|
||||
obj := b.GetObject()
|
||||
obj.input = make(chan *Context)
|
||||
go func() {
|
||||
for c := range obj.input {
|
||||
b.Update(c)
|
||||
e.wg.Done()
|
||||
}
|
||||
}()
|
||||
e.Objects.store = append(e.Objects.store, b)
|
||||
// Add new objects to the Engine's view.
|
||||
func (e *Engine) Spawn(object Object) bool {
|
||||
|
||||
return nil
|
||||
ctx := Context{
|
||||
engine: e,
|
||||
}
|
||||
object.OnStart(ctx)
|
||||
ok := e.objects.add(object)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// Delete object from Engine.
|
||||
func (e *Engine) Del(b Objecter) error {
|
||||
/*if !e.Objects.Has(b) {
|
||||
return ObjectNotExistErr
|
||||
func (e *Engine) Delete(object Object) bool {
|
||||
|
||||
ctx := Context{
|
||||
engine: e,
|
||||
}
|
||||
object.OnDelete(ctx)
|
||||
ok := e.objects.remove(object)
|
||||
|
||||
b.Delete(&Context{Engine: e})
|
||||
e.Objects.Del(b)*/
|
||||
|
||||
return nil
|
||||
return ok
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -184,7 +168,7 @@ var (
|
|||
)
|
||||
|
||||
func (e *Engine) IsPressed(k Key) bool {
|
||||
keys := e.Keys()
|
||||
keys := e.GetKeyboardKeys()
|
||||
for _, v := range keys {
|
||||
if v == k {
|
||||
return true
|
||||
|
@ -199,109 +183,191 @@ func (e *Engine) IsButtoned(b MouseButton) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
func (e *Engine) Wheel() Vector {
|
||||
func (e *Engine) GetMouseWheel() mx.Vector {
|
||||
return e.wheel
|
||||
}
|
||||
|
||||
func (e *Engine) cursorPosition() Vector {
|
||||
func (e *Engine) cursorPosition() mx.Vector {
|
||||
x, y := ebiten.CursorPosition()
|
||||
return V(Float(x), Float(y))
|
||||
return mx.Vector{mx.Float(x), mx.Float(y)}
|
||||
}
|
||||
|
||||
func (e *Engine) CursorPosition() Vector {
|
||||
// Get the real cursor position.
|
||||
func (e *Engine) GetRealCursorPosition() mx.Vector {
|
||||
return e.cursorPos
|
||||
}
|
||||
|
||||
func (e *Engine) AbsCursorPosition() Vector {
|
||||
return e.CursorPosition().Apply(e.Camera.AbsMatrix())
|
||||
// Get the absolute cursor position in the world
|
||||
// of the engine.
|
||||
func (e *Engine) GetAbsCursorPosition(
|
||||
) mx.Vector {
|
||||
return e.GetRealCursorPosition().
|
||||
Apply(e.camera.GetAbsMatrice(Context{
|
||||
engine: e,
|
||||
}))
|
||||
}
|
||||
|
||||
func (e *engine) Update() error {
|
||||
// Get the real window size in the current context.
|
||||
func (e *Engine) GetRealWinSize() mx.Vector {
|
||||
var w, h int
|
||||
if e.wcfg.Fullscreen {
|
||||
w, h = ebiten.ScreenSizeInFullscreen()
|
||||
} else {
|
||||
w, h = e.wcfg.Width, e.wcfg.Height
|
||||
}
|
||||
return mx.Vector{
|
||||
mx.Float(w),
|
||||
mx.Float(h),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Engine) GetAbsWinSize() mx.Vector {
|
||||
return e.camera.GetAbsWinSize(Context{
|
||||
engine: e,
|
||||
})
|
||||
}
|
||||
func (e *Engine) Runes() []rune {
|
||||
return e.runes
|
||||
}
|
||||
|
||||
func (e *engine) updateEvents() Events {
|
||||
|
||||
eng := (*Engine)(e)
|
||||
|
||||
// Buffering the context for faster.
|
||||
events := Events{}
|
||||
|
||||
e.prevKeys = e.keys
|
||||
e.keys = inpututil.
|
||||
AppendPressedKeys(e.keys[:0])
|
||||
|
||||
events := []any{}
|
||||
// Mouse buttons.
|
||||
btns := e.buttons
|
||||
for _, btn := range allButtons {
|
||||
if inpututil.IsMouseButtonJustPressed(btn) {
|
||||
btns[btn] = struct{}{}
|
||||
events = append(events, &MouseButtonDown{
|
||||
MouseButton: btn,
|
||||
})
|
||||
events.Mouse.Downs = append(
|
||||
events.Mouse.Downs,
|
||||
MouseButtonDown{
|
||||
MouseButton: btn,
|
||||
},
|
||||
)
|
||||
} else if inpututil.IsMouseButtonJustReleased(btn) {
|
||||
delete(btns, btn)
|
||||
events = append(events, &MouseButtonUp{
|
||||
MouseButton: btn,
|
||||
})
|
||||
events.Mouse.Ups = append(
|
||||
events.Mouse.Ups,
|
||||
MouseButtonUp{
|
||||
MouseButton: btn,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse wheel.
|
||||
x, y := ebiten.Wheel()
|
||||
eng.wheel = V(x, y)
|
||||
if !(eng.wheel.Eq(ZV)) {
|
||||
events = append(events, &WheelChange{
|
||||
Offset: eng.wheel,
|
||||
})
|
||||
}
|
||||
|
||||
keyDiff := diffEm(e.prevKeys, e.keys)
|
||||
for _, key := range keyDiff {
|
||||
var event any
|
||||
if eng.IsPressed(key) {
|
||||
event = &KeyDown{
|
||||
Key: key,
|
||||
}
|
||||
} else {
|
||||
event = &KeyUp{
|
||||
Key: key,
|
||||
}
|
||||
e.wheel = mx.Vector{x, y}
|
||||
if !(e.wheel.Eq(mx.ZV)) {
|
||||
events.Mouse.Wheel = &WheelChange{
|
||||
Offset: e.wheel,
|
||||
}
|
||||
events = append(events, event)
|
||||
}
|
||||
|
||||
// Cursor position.
|
||||
realPos := eng.cursorPosition()
|
||||
if !realPos.Eq(eng.cursorPos) {
|
||||
absM := eng.Camera.AbsMatrix()
|
||||
if !realPos.Eq(e.cursorPos) {
|
||||
absM := eng.camera.GetAbsMatrice(Context{
|
||||
engine: eng,
|
||||
})
|
||||
|
||||
absPrevPos :=eng.cursorPos.Apply(absM)
|
||||
absPrevPos := e.cursorPos.Apply(absM)
|
||||
absPos := realPos.Apply(absM)
|
||||
|
||||
events = append(events, &MouseMove{
|
||||
Real: realPos.Sub(eng.cursorPos),
|
||||
Abs: absPos.Sub(absPrevPos),
|
||||
})
|
||||
eng.cursorPos = realPos
|
||||
events.Mouse.Move = &MouseMove{
|
||||
RealDelta: realPos.Sub(e.cursorPos),
|
||||
AbsDelta: absPos.Sub(absPrevPos),
|
||||
}
|
||||
e.cursorPos = realPos
|
||||
}
|
||||
|
||||
e.prevKeys = e.keys
|
||||
//newKeys := []Key{e.keys[0]}
|
||||
e.keys = nil
|
||||
e.keys = inpututil.
|
||||
AppendPressedKeys(e.keys[:0])
|
||||
|
||||
// Keyboard.
|
||||
keyDiff := diffEm(e.prevKeys, e.keys)
|
||||
for _, key := range keyDiff {
|
||||
if eng.IsPressed(key) {
|
||||
events.Keyboard.Downs = append(
|
||||
events.Keyboard.Downs,
|
||||
KeyDown{
|
||||
Key: key,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
events.Keyboard.Ups = append(
|
||||
events.Keyboard.Ups,
|
||||
KeyUp{
|
||||
Key: key,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
func (e *engine) Update() error {
|
||||
|
||||
eng := (*Engine)(e)
|
||||
e.dt = time.Since(e.lastTime)
|
||||
|
||||
e.runes = ebiten.AppendInputChars(e.runes[:0])
|
||||
//fmt.Println("runes:", e.runes)
|
||||
// Buffering the context for faster.
|
||||
// Providing the events to the objects.
|
||||
// Maybe should think of the better way,
|
||||
// but for it is simple enough.
|
||||
c := &Context{
|
||||
Engine: eng,
|
||||
typ: updateContext,
|
||||
Events: events,
|
||||
}
|
||||
for _, object := range e.Objects.store {
|
||||
e.wg.Add(1)
|
||||
object.Input() <- c
|
||||
}
|
||||
e.wg.Wait()
|
||||
|
||||
events := e.updateEvents()
|
||||
|
||||
c := Context{
|
||||
engine: eng,
|
||||
events: events,
|
||||
}
|
||||
|
||||
// Should think of the order?
|
||||
e.objects.updateTags(c)
|
||||
e.objects.updateObjects(c)
|
||||
|
||||
e.lastTime = time.Now()
|
||||
e.frame++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *engine) Draw(i *ebiten.Image) {
|
||||
e.dt = time.Since(e.lastTime).Seconds()
|
||||
func (e *Engine) Objects() *Objects {
|
||||
return e.objects
|
||||
}
|
||||
|
||||
|
||||
var (
|
||||
fullPageIndexes = func() [MaxVertices]uint16 {
|
||||
ret := [MaxVertices]uint16{}
|
||||
for i:=0 ; i<len(ret) ; i++ {
|
||||
ret[i] = uint16(i)
|
||||
}
|
||||
return ret
|
||||
}()
|
||||
defaultPageImg = func() *Image {
|
||||
img := NewImage(1, 1)
|
||||
img.Set(0, 0, RGBA(1, 1, 1, 1))
|
||||
return img
|
||||
}()
|
||||
defaultTriOptions = &ebiten.DrawTrianglesOptions{}
|
||||
)
|
||||
func (e *engine) Draw(img *ebiten.Image) {
|
||||
e.drawdt = time.Since(e.drawLastTime)
|
||||
eng := (*Engine)(e)
|
||||
m := map[Layer][]Drawer{}
|
||||
for _, object := range eng.Objects.store {
|
||||
for _, object := range eng.objects.store {
|
||||
// Skipping the ones we do not need to draw.
|
||||
if !object.IsVisible() {
|
||||
if object == nil || !object.IsVisible() {
|
||||
continue
|
||||
}
|
||||
l := object.GetLayer()
|
||||
|
@ -315,18 +381,49 @@ func (e *engine) Draw(i *ebiten.Image) {
|
|||
m[l] = append(layer, object)
|
||||
}
|
||||
|
||||
// Drawing layers.
|
||||
// Drawing layers via the sparse array.
|
||||
// First drawing via the inside function
|
||||
// and then the returned []EVertex.
|
||||
layers := maps.NewSparse[Layer, []Drawer](nil, m)
|
||||
c := &Context{Engine: eng, typ: drawContext, Image: i}
|
||||
c := Context{engine: eng, image: img}
|
||||
for layer := range layers.Chan() {
|
||||
|
||||
vertices := Vertices{}
|
||||
for _, drawer := range layer {
|
||||
drawer.Draw(c)
|
||||
drawing := drawer.Draw(c)
|
||||
if drawing != nil {
|
||||
vertices = append(
|
||||
vertices,
|
||||
drawing.Vertices...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pn := len(vertices) / MaxVertices
|
||||
mod := len(vertices) % MaxVertices
|
||||
for i := 0 ; i<pn ; i++ {
|
||||
cur := i*MaxVertices
|
||||
img.DrawTriangles(
|
||||
vertices[cur:cur+MaxVertices].ToAPI(),
|
||||
fullPageIndexes[:],
|
||||
defaultPageImg,
|
||||
defaultTriOptions,
|
||||
)
|
||||
}
|
||||
|
||||
st := pn*MaxVertices
|
||||
img.DrawTriangles(
|
||||
vertices[st:].ToAPI(),
|
||||
fullPageIndexes[:mod],
|
||||
defaultPageImg,
|
||||
defaultTriOptions,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Empty the buff to generate it again.
|
||||
eng.Camera.buffered = false
|
||||
e.lastTime = time.Now()
|
||||
e.drawLastTime = time.Now()
|
||||
e.dframe++
|
||||
}
|
||||
|
||||
func (e *engine) Layout(ow, oh int) (int, int) {
|
||||
|
@ -337,18 +434,45 @@ func (e *engine) Layout(ow, oh int) (int, int) {
|
|||
return ow, oh
|
||||
}
|
||||
|
||||
// Return the delta time duration value.
|
||||
func (e *Engine) DT() Float {
|
||||
// Return the delta time between Draw calls.
|
||||
func (e *Engine) DrawDT() Duration {
|
||||
return e.drawdt
|
||||
}
|
||||
|
||||
// Current Drawing frame.
|
||||
func (e *Engine) Dframe() uint {
|
||||
return e.dframe
|
||||
}
|
||||
|
||||
// Return the real delta time.
|
||||
// Please, prefer the DT().
|
||||
func (e *Engine) RealDT() Duration {
|
||||
return e.dt
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Current frame
|
||||
func (e *Engine) Frame() uint {
|
||||
return e.frame
|
||||
}
|
||||
|
||||
// Current FPS.
|
||||
func (e *Engine) FPS() float64 {
|
||||
return ebiten.ActualFPS()
|
||||
}
|
||||
|
||||
// Current TPS.
|
||||
func (e *Engine) TPS() float64 {
|
||||
return ebiten.ActualTPS()
|
||||
}
|
||||
|
||||
// Run the engine.
|
||||
func (e *Engine) Run() error {
|
||||
ebiten.ReadDebugInfo(&e.wcfg.DebugInfo)
|
||||
ebiten.SetWindowTitle(e.wcfg.Title)
|
||||
|
@ -363,3 +487,6 @@ func (e *Engine) Run() error {
|
|||
return ebiten.RunGameWithOptions((*engine)(e), e.wcfg.Options)
|
||||
}
|
||||
|
||||
func (e *Engine) GetWindowConfig() WindowConfig {
|
||||
return e.wcfg
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ObjectExistErr = errors.New("the object already exists")
|
||||
ObjectNotExistErr = errors.New("the object does not exist")
|
||||
ObjectNotImplementedErr = errors.New("none of object methods are implemented")
|
||||
ErrObjectExist = errors.New("the object already exists")
|
||||
ErrObjectNotExist = errors.New("the object does not exist")
|
||||
)
|
||||
|
||||
|
|
42
event.go
42
event.go
|
@ -4,6 +4,8 @@ import (
|
|||
//"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
func diffEm[V comparable](s1, s2 []V) []V {
|
||||
combinedSlice := append(s1, s2...)
|
||||
dm := make(map[V]int)
|
||||
|
@ -25,31 +27,61 @@ func diffEm[V comparable](s1, s2 []V) []V {
|
|||
return retSlice
|
||||
}
|
||||
|
||||
// The type represents event of a
|
||||
// key getting pressed down.
|
||||
type KeyDown struct {
|
||||
Key
|
||||
}
|
||||
|
||||
// The type represents event of a
|
||||
// key getting pressed up.
|
||||
type KeyUp struct {
|
||||
Key
|
||||
}
|
||||
|
||||
// The type represents event of a
|
||||
// mouse button being pressed down.
|
||||
type MouseButtonDown struct {
|
||||
MouseButton
|
||||
P Vector
|
||||
Position mx.Vector
|
||||
}
|
||||
|
||||
// The type represents event of a
|
||||
// mouse button being pressed up.
|
||||
type MouseButtonUp struct {
|
||||
MouseButton
|
||||
P Vector
|
||||
Positon mx.Vector
|
||||
}
|
||||
|
||||
// The type represents
|
||||
// event of moving the mouse.
|
||||
type MouseMove struct {
|
||||
// Real and absolute deltas.
|
||||
Real, Abs Vector
|
||||
// Real and absolute deltas
|
||||
// for the mouse movement.
|
||||
RealDelta, AbsDelta mx.Vector
|
||||
}
|
||||
|
||||
// The type represents event
|
||||
// of a wheel change.
|
||||
type WheelChange struct {
|
||||
Offset Vector
|
||||
Offset mx.Vector
|
||||
}
|
||||
|
||||
type KeyboardEvents struct {
|
||||
Downs []KeyDown
|
||||
Ups []KeyUp
|
||||
}
|
||||
|
||||
type MouseEvents struct {
|
||||
Downs []MouseButtonDown
|
||||
Ups []MouseButtonUp
|
||||
Move *MouseMove
|
||||
Wheel *WheelChange
|
||||
}
|
||||
|
||||
type Events struct {
|
||||
Keyboard KeyboardEvents
|
||||
Mouse MouseEvents
|
||||
}
|
||||
|
||||
type EventChan chan any
|
||||
|
|
13
fmt.go
Normal file
13
fmt.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Println(v ...any) {
|
||||
fmt.Println(v...)
|
||||
}
|
||||
|
||||
func Printf(format string, v ...any) {
|
||||
fmt.Printf(format, v...)
|
||||
}
|
23
go.mod
23
go.mod
|
@ -1,22 +1,21 @@
|
|||
module vultras.su/core/gg
|
||||
module surdeus.su/core/gg
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.3
|
||||
|
||||
require (
|
||||
github.com/di4f/gods v0.0.0-20231214190239-d523423d8d5e
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.0-alpha.3.0.20230521122940-90562ee84b9b
|
||||
github.com/silbinarywolf/preferdiscretegpu v1.0.0
|
||||
golang.org/x/image v0.7.0
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.3
|
||||
golang.org/x/image v0.15.0
|
||||
surdeus.su/core/gods v0.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ebitengine/purego v0.4.0-alpha.4 // indirect
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect
|
||||
github.com/jezek/xgb v1.1.0 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/mobile v0.0.0-20230427221453-e8d11dd0ba41 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
github.com/ebitengine/purego v0.5.1 // indirect
|
||||
github.com/jezek/xgb v1.1.1 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20240112132812-db7319d0e0e3 // indirect
|
||||
golang.org/x/mobile v0.0.0-20240112133503-c713f31d574b // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
|
|
73
go.sum
73
go.sum
|
@ -1,53 +1,20 @@
|
|||
github.com/di4f/gods v0.0.0-20231214190239-d523423d8d5e h1:LdL9GpXPJikQ7DXQZFf5Zz5h5MYAS9R7itEztnscku8=
|
||||
github.com/di4f/gods v0.0.0-20231214190239-d523423d8d5e/go.mod h1:5GbuWHGgBJIIhBiQB/00flvo3k421S8O1XvmrXKWO2U=
|
||||
github.com/ebitengine/purego v0.4.0-alpha.4 h1:Y7yIV06Yo5M2BAdD7EVPhfp6LZ0tEcQo5770OhYUVes=
|
||||
github.com/ebitengine/purego v0.4.0-alpha.4/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.0-alpha.3.0.20230521122940-90562ee84b9b h1:QURPKDDBduvp9Zh5/4NeiPeUfpB4PokrfoH3tx4yNrg=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.0-alpha.3.0.20230521122940-90562ee84b9b/go.mod h1:+fFI6Ag5YvbX1ivNQD2TxNhpWFDPuxEoew421TTQAxI=
|
||||
github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk=
|
||||
github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||
github.com/silbinarywolf/preferdiscretegpu v1.0.0 h1:tuvXLRCnoFMFyk74/8PFvO0B5rjDmXm0JgNTaOYAHT0=
|
||||
github.com/silbinarywolf/preferdiscretegpu v1.0.0/go.mod h1:h3s2GkfAP2sWqoS7v/PxAlFOQ1azMRsZxUJNw47QhLc=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp/shiny v0.0.0-20230522175609-2e198f4a06a1 h1:NxHSRPlbeyFGDc6rU7YsvxV/4bXS9XhuvUt5pP63XUs=
|
||||
golang.org/x/exp/shiny v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0=
|
||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||
golang.org/x/mobile v0.0.0-20230427221453-e8d11dd0ba41 h1:539vykMVJsmdiucRtMmdeLLZaTVhWhaAHFcPabj2lws=
|
||||
golang.org/x/mobile v0.0.0-20230427221453-e8d11dd0ba41/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
github.com/ebitengine/purego v0.5.1 h1:hNunhThpOf1vzKl49v6YxIsXLhl92vbBEv1/2Ez3ZrY=
|
||||
github.com/ebitengine/purego v0.5.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.3 h1:xJ5klESxhflZbPUx3GdIPoITzgPgamsyv8aZCVguXGI=
|
||||
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/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||
golang.org/x/exp/shiny v0.0.0-20240112132812-db7319d0e0e3 h1:NezsOJwoBjJ5AXH5QQCdxe+WsqLw+f/t8eo1Tacfhqs=
|
||||
golang.org/x/exp/shiny v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
|
||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/mobile v0.0.0-20240112133503-c713f31d574b h1:kfWLZgb8iUBHdE9WydD5V5dHIS/F6HjlBZNyJfn2bs4=
|
||||
golang.org/x/mobile v0.0.0-20240112133503-c713f31d574b/go.mod h1:4efzQnuA1nICq6h4kmZRMGzbPiP06lZvgADUu1VpJCE=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
surdeus.su/core/gods v0.0.1 h1:0hnecGIfWSUG2q2Y9RSuXuAZLD2FCGAvI3lB/xU511Y=
|
||||
surdeus.su/core/gods v0.0.1/go.mod h1:qf/FOAiSKLwVxmHj3EwCgvqmu9DeYAY4AmVh071E9zc=
|
||||
|
|
29
img.go
29
img.go
|
@ -7,30 +7,35 @@ import (
|
|||
"io"
|
||||
"math"
|
||||
)
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
type Image = ebiten.Image
|
||||
type ImageRect = image.Rectangle
|
||||
type ImagePoint = image.Point
|
||||
|
||||
type ColorV uint32
|
||||
type ColorM = ebiten.ColorM
|
||||
type ColorValue uint32
|
||||
type ColorMode = ebiten.ColorM
|
||||
type Color struct {
|
||||
R, G, B, A ColorV
|
||||
R, G, B, A ColorValue
|
||||
}
|
||||
|
||||
const (
|
||||
MaxColorV = math.MaxUint32
|
||||
MaxColorValue = math.MaxUint32
|
||||
)
|
||||
|
||||
// The wrapper to make RGBA color via
|
||||
// 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 {
|
||||
ColorV(r*MaxColorV),
|
||||
ColorV(g*MaxColorV),
|
||||
ColorV(b*MaxColorV),
|
||||
ColorV(a*MaxColorV),
|
||||
ColorValue(r*MaxColorValue),
|
||||
ColorValue(g*MaxColorValue),
|
||||
ColorValue(b*MaxColorValue),
|
||||
ColorValue(a*MaxColorValue),
|
||||
}
|
||||
}
|
||||
|
||||
// Read an image from Reader and return
|
||||
// a new *Image.
|
||||
func LoadImage(input io.Reader) (*Image, error) {
|
||||
img, _, err := image.Decode(input)
|
||||
if err != nil {
|
||||
|
@ -41,12 +46,18 @@ func LoadImage(input io.Reader) (*Image, error) {
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
// Returns a new empty image
|
||||
// with specified with and height.
|
||||
func NewImage(w, h int) (*Image) {
|
||||
return ebiten.NewImage(w, h)
|
||||
}
|
||||
|
||||
func NewImageFromImage(img image.Image) *Image {
|
||||
return ebiten.NewImageFromImage(img)
|
||||
}
|
||||
|
||||
func (c Color) RGBA() (r, g, b, a uint32) {
|
||||
return uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
|
||||
|
||||
}
|
||||
|
||||
|
|
56
keep.go
Normal file
56
keep.go
Normal 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()
|
||||
}*/
|
9
layer.go
Normal file
9
layer.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package gg
|
||||
|
||||
// The type represents order of drawing.
|
||||
// Higher values are drawn later.
|
||||
type Layer float64
|
||||
|
||||
func (l Layer) GetLayer() Layer {
|
||||
return l
|
||||
}
|
153
line.go
153
line.go
|
@ -1,153 +0,0 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
"math"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
// The type represents mathematical equation of line and line itself.
|
||||
type Line struct {
|
||||
K, C Float
|
||||
}
|
||||
|
||||
type Liner interface {
|
||||
Line() Line
|
||||
}
|
||||
|
||||
type LinerPointContainer interface {
|
||||
Liner
|
||||
PointContainer
|
||||
}
|
||||
|
||||
// The type represents a line segment.
|
||||
type LineSegment [2]Point
|
||||
|
||||
// The type represents multiple line segments.
|
||||
type LineSegments []LineSegment
|
||||
|
||||
type Edge = LineSegment
|
||||
type Edges = LineSegments
|
||||
|
||||
|
||||
|
||||
// Check if two LinerPointContainers do cross and return the
|
||||
// crossing point.
|
||||
func LinersCross(lp1, lp2 LinerPointContainer) (Point, bool) {
|
||||
l1 := lp1.Line()
|
||||
l2 := lp2.Line()
|
||||
|
||||
p, crosses := l1.crossesLine(l2)
|
||||
if !crosses ||
|
||||
!lp1.ContainsPoint(p) ||
|
||||
!lp2.ContainsPoint(p) {
|
||||
return Point{}, false
|
||||
}
|
||||
|
||||
return p, true
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 math.Atan(l1.K/l2.K)
|
||||
}
|
||||
|
||||
// Returns the line itself. Made to implement the Liner interface.
|
||||
func (l Line) Line() Line {
|
||||
return l
|
||||
}
|
||||
|
||||
// Returns corresponding to the segment line line.
|
||||
func (l LineSegment) Line() Line {
|
||||
p0 := l[0]
|
||||
p1 := l[1]
|
||||
|
||||
k := (p0.Y - p1.Y) / (p0.X - p1.X)
|
||||
c := p0.Y - p0.X*k
|
||||
|
||||
return Line{k, c}
|
||||
}
|
||||
|
||||
func (l Line) ContainsPoint(p Point) bool {
|
||||
buf := Line{0, p.Y}
|
||||
pc, ok := l.crossesLine(buf)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return pc == p
|
||||
}
|
||||
|
||||
func (l LineSegment) ContainsPoint(p Point) bool {
|
||||
line := l.Line()
|
||||
if !line.ContainsPoint(p) {
|
||||
return false
|
||||
}
|
||||
|
||||
xMax := Max(l[0].X, l[1].X)
|
||||
xMin := Min(l[0].X, l[1].X)
|
||||
|
||||
yMax := Max(l[0].Y, l[1].Y)
|
||||
yMin := Min(l[0].Y, l[1].Y)
|
||||
|
||||
if !(xMin < p.X && p.X < xMax) ||
|
||||
!(yMin < p.Y && p.Y < yMax) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (l1 Line) crossesLine(l2 Line) (Point, bool) {
|
||||
if LinersParallel(l1, l2) {
|
||||
return Point{}, false
|
||||
}
|
||||
|
||||
x := (l1.C - l2.C) / (l2.K - l1.K)
|
||||
y := l1.K*x + l1.C
|
||||
return Point{x, y}, true
|
||||
}
|
||||
|
||||
|
||||
// Get square of length of line segment.
|
||||
func (ls LineSegment) LenSqr() Float {
|
||||
return Sqr(ls[0].X - ls[1].X) +
|
||||
Sqr(ls[0].Y - ls[1].Y)
|
||||
}
|
||||
|
||||
// Get length of the line segment.
|
||||
func (ls LineSegment) Len() Float {
|
||||
return math.Sqrt(ls.LenSqr())
|
||||
}
|
||||
|
||||
func (what LineSegments) Cross(with LineSegments) ([][2]int, Points) {
|
||||
indexes := [][2]int{}
|
||||
points := Points{}
|
||||
for i := range what {
|
||||
for j := range with {
|
||||
p, cross := LinersCross(what[i], with[j])
|
||||
if cross {
|
||||
points = append(points, p)
|
||||
indexes = append(indexes, [2]int{i, j})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return indexes, points
|
||||
}
|
||||
|
67
math.go
67
math.go
|
@ -1,71 +1,8 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
"math"
|
||||
//"surdeus.su/core/gg/mx"
|
||||
)
|
||||
|
||||
// The type is used in all Engine interactions
|
||||
// where you need floating values.
|
||||
type Float = float64
|
||||
|
||||
const (
|
||||
MaxFloat = math.MaxFloat64
|
||||
Pi = math.Pi
|
||||
RadDegrees = 57.2958
|
||||
//PiRad = Pi * Rad
|
||||
)
|
||||
|
||||
func Degree(f Float) Float {
|
||||
return (f/(2*Pi))*360
|
||||
}
|
||||
|
||||
// Returns square of the value.
|
||||
func Sqr(v Float) Float {
|
||||
return v * v
|
||||
}
|
||||
|
||||
func Asin(v Float) Float {
|
||||
return math.Asin(v)
|
||||
}
|
||||
|
||||
func Atan(v Float) Float {
|
||||
return math.Atan(v)
|
||||
}
|
||||
|
||||
func Sgn(v Float) Float {
|
||||
if v > 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
if v < 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func Max(v1, v2 Float) Float {
|
||||
if v1 > v2 {
|
||||
return v1
|
||||
}
|
||||
|
||||
return v2
|
||||
}
|
||||
|
||||
func Min(v1, v2 Float) Float {
|
||||
if v1 < v2 {
|
||||
return v1
|
||||
}
|
||||
|
||||
return v2
|
||||
}
|
||||
|
||||
func RadiansToDegrees(v Float) Float {
|
||||
return v/Pi * 180
|
||||
}
|
||||
|
||||
func DeegresToRadians(v Float) Float {
|
||||
return v
|
||||
}
|
||||
|
||||
//type Float = mx.Float
|
||||
|
||||
|
|
BIN
media/player.png
Normal file
BIN
media/player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
32
mx/circle.go
Normal file
32
mx/circle.go
Normal 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
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package gg
|
||||
package mx
|
||||
|
||||
// The structure represents elipses.
|
||||
type Elipse struct {
|
||||
/*type Elipse struct {
|
||||
// In transform S.X and S.Y represent
|
||||
// coefficents for the corresponding axises.
|
||||
Transform
|
||||
|
@ -9,5 +9,5 @@ type Elipse struct {
|
|||
|
||||
func (e Elipse) ContainsPoint(p Point) bool {
|
||||
return true
|
||||
}
|
||||
}*/
|
||||
|
69
mx/line.go
Normal file
69
mx/line.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package mx
|
||||
|
||||
// The type represents a line segment. The name is for short.
|
||||
type Line [2]Vector
|
||||
type Lines []Line
|
||||
|
||||
// Returns corresponding to the segment Line.
|
||||
func (l Line) GetLineExpr() LineExpr {
|
||||
var (
|
||||
x Float
|
||||
vertical bool
|
||||
)
|
||||
|
||||
p0 := l[0]
|
||||
p1 := l[1]
|
||||
|
||||
k := (p0.Y - p1.Y) / (p0.X - p1.X)
|
||||
c := p0.Y - p0.X*k
|
||||
if p0.X == p1.X {
|
||||
x = p0.X
|
||||
vertical = true
|
||||
}
|
||||
|
||||
return LineExpr{k, c, x, vertical}
|
||||
}
|
||||
|
||||
// Returns the points that the Line contains.
|
||||
func (l Line) GetContainedPoints(pts Vectors) Vectors {
|
||||
ret := make(Vectors, 0, len(pts))
|
||||
for i := range pts {
|
||||
if l.ContainsPoint(pts[i]) {
|
||||
ret = append(ret, pts[i])
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Check if the edge contains the specified point.
|
||||
func (l Line) ContainsPoint(p Vector) bool {
|
||||
lexpr := l.GetLineExpr()
|
||||
if !lexpr.ContainsPoint(p) {
|
||||
return false
|
||||
}
|
||||
|
||||
xMax := Max(l[0].X, l[1].X)
|
||||
xMin := Min(l[0].X, l[1].X)
|
||||
|
||||
yMax := Max(l[0].Y, l[1].Y)
|
||||
yMin := Min(l[0].Y, l[1].Y)
|
||||
|
||||
if !(xMin <= p.X && p.X <= xMax) ||
|
||||
!(yMin <= p.Y && p.Y <= yMax) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Get square of length of line segment (for performance sometimes).
|
||||
func (ls Line) LenSqr() Float {
|
||||
return Sqr(ls[0].X - ls[1].X) +
|
||||
Sqr(ls[0].Y - ls[1].Y)
|
||||
}
|
||||
|
||||
// Get length of the line segment.
|
||||
func (ls Line) Len() Float {
|
||||
return Sqrt(ls.LenSqr())
|
||||
}
|
||||
|
61
mx/line_expr.go
Normal file
61
mx/line_expr.go
Normal 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
81
mx/liner.go
Normal 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)
|
||||
}
|
97
mx/math.go
Normal file
97
mx/math.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package mx
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// The type is used in all Engine interactions
|
||||
// where you need floating values to keep
|
||||
// it interchangable in future.
|
||||
// Not much promise for this though.
|
||||
type Float = float64
|
||||
|
||||
const (
|
||||
MaxFloat Float = math.MaxFloat64
|
||||
Pi Float = math.Pi
|
||||
RadDegrees Float = 57.2958
|
||||
//PiRad = Pi * Rad
|
||||
EqualityThreshold Float = 1e-9
|
||||
)
|
||||
|
||||
func IsNan(v Float) bool {
|
||||
return math.IsNaN(v)
|
||||
}
|
||||
|
||||
func IsInf(f Float, sign int) bool {
|
||||
return math.IsInf(f, sign)
|
||||
}
|
||||
|
||||
func Degree(f Float) Float {
|
||||
return (f/(2*Pi))*360
|
||||
}
|
||||
|
||||
// Returns square of the value.
|
||||
func Sqr(v Float) Float {
|
||||
return v * v
|
||||
}
|
||||
|
||||
func Sqrt(v Float) Float {
|
||||
return math.Sqrt(v)
|
||||
}
|
||||
|
||||
func Asin(v Float) Float {
|
||||
return math.Asin(v)
|
||||
}
|
||||
|
||||
func Atan(v Float) Float {
|
||||
return math.Atan(v)
|
||||
}
|
||||
|
||||
func Sgn(v Float) Float {
|
||||
if v > 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
if v < 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func Max(v1, v2 Float) Float {
|
||||
if v1 > v2 {
|
||||
return v1
|
||||
}
|
||||
|
||||
return v2
|
||||
}
|
||||
|
||||
func Min(v1, v2 Float) Float {
|
||||
if v1 < v2 {
|
||||
return v1
|
||||
}
|
||||
|
||||
return v2
|
||||
}
|
||||
|
||||
func Abs(v Float) Float {
|
||||
if v >= 0 {
|
||||
return v
|
||||
}
|
||||
return -v
|
||||
}
|
||||
|
||||
func RadiansToDegrees(v Float) Float {
|
||||
return v/Pi * 180
|
||||
}
|
||||
|
||||
func DeegresToRadians(v Float) Float {
|
||||
return v
|
||||
}
|
||||
|
||||
// Returns whether the two floats are nearly equal.
|
||||
func Neq(a, b Float) bool {
|
||||
return Abs(a-b) <= EqualityThreshold
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
package gg
|
||||
package mx
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
//"math"
|
||||
)
|
||||
|
||||
type Matrix = ebiten.GeoM
|
||||
type Matrice = ebiten.GeoM
|
||||
|
10
mx/point.go
Normal file
10
mx/point.go
Normal 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
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package gg
|
||||
package mx
|
||||
|
||||
// The type represents math ray.
|
||||
type Ray struct {
|
||||
// The start point.
|
||||
P Point
|
||||
Start Vector
|
||||
// Rotation of the ray.
|
||||
R Float
|
||||
Rotaton Float
|
||||
}
|
||||
|
18
mx/size.go
Normal file
18
mx/size.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package mx
|
||||
|
||||
// The type describes a simple
|
||||
// rectangle without transformations.
|
||||
type Size struct {
|
||||
// The upper left corner position point.
|
||||
Position Vector
|
||||
// Absolute width and height.
|
||||
// Both must be positive values.
|
||||
Width, Height Float
|
||||
}
|
||||
|
||||
func (size Size) ContainsPoint(pt Vector) bool {
|
||||
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 )
|
||||
}
|
||||
|
||||
//type (s Size) ContainsPoint
|
115
mx/triangle.go
Normal file
115
mx/triangle.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
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) ContainsPoint(pt Vector) bool {
|
||||
for _, t := range ts {
|
||||
if t.ContainsPoint(pt) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
112
mx/vector.go
Normal file
112
mx/vector.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
package mx
|
||||
|
||||
import (
|
||||
//"github.com/hajimehoshi/ebiten/v2"
|
||||
"math"
|
||||
)
|
||||
|
||||
var (
|
||||
ZV = V2(0)
|
||||
)
|
||||
|
||||
type Vector struct {
|
||||
X, Y Float
|
||||
|
||||
}
|
||||
type Vectors []Vector
|
||||
|
||||
func (v Vector) XY() (Float, Float) {
|
||||
return v.X, v.Y
|
||||
}
|
||||
|
||||
func V(x, y Float) Vector {
|
||||
return Vector{x, y}
|
||||
}
|
||||
|
||||
func V2(v Float) Vector {
|
||||
return Vector{v, v}
|
||||
}
|
||||
|
||||
func VX(x Float) Vector {
|
||||
return Vector{x, 0}
|
||||
}
|
||||
|
||||
func VY(y Float) Vector {
|
||||
return Vector{0, y}
|
||||
}
|
||||
|
||||
func (v Vector) Div(o Vector) Vector {
|
||||
return V(
|
||||
v.X / o.X,
|
||||
v.Y / o.Y,
|
||||
)
|
||||
}
|
||||
|
||||
func (v Vector) Mul(o Vector) Vector {
|
||||
return V(
|
||||
v.X * o.X,
|
||||
v.Y * o.Y,
|
||||
)
|
||||
}
|
||||
|
||||
func (v Vector) Eq(o Vector) bool {
|
||||
return v.X == o.X && v.Y == o.Y
|
||||
}
|
||||
|
||||
// Returns the vector with the matrix applied
|
||||
func (v Vector) Apply(m Matrice) Vector {
|
||||
x, y := m.Apply(v.X, v.Y)
|
||||
return Vector{x, y}
|
||||
}
|
||||
|
||||
// Adds the vector to other one returning the result.
|
||||
func (v Vector) Add(a Vector) Vector {
|
||||
v.X += a.X
|
||||
v.Y += a.Y
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Returns the subtraction of all the vectors from the current one.
|
||||
func (v Vector) Sub(s Vector) Vector {
|
||||
v.X -= s.X
|
||||
v.Y -= s.Y
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Returns the negative version of the vector.
|
||||
func (v Vector) Neg() Vector {
|
||||
return Vector{
|
||||
-v.X,
|
||||
-v.Y,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the vector rotated by "a" angle in radians.
|
||||
func (v Vector) Rot(a Float) Vector {
|
||||
m := Matrice{}
|
||||
m.Rotate(a)
|
||||
return v.Apply(m)
|
||||
}
|
||||
|
||||
// Returns the normalized vector.
|
||||
func (v Vector) Norm() Vector {
|
||||
l := math.Sqrt(v.X*v.X + v.Y*v.Y)
|
||||
return V(v.X / l, v.Y / l)
|
||||
}
|
||||
|
||||
func (pts Vectors) PointsContainedIn(
|
||||
c PointContainer,
|
||||
) Vectors {
|
||||
ret := make(Vectors, 0, len(pts))
|
||||
|
||||
for _, pt := range pts {
|
||||
if c.ContainsPoint(pt) {
|
||||
ret = append(ret, pt)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
2
mx/vertice.go
Normal file
2
mx/vertice.go
Normal file
|
@ -0,0 +1,2 @@
|
|||
package mx
|
||||
|
73
object.go
73
object.go
|
@ -1,71 +1,14 @@
|
|||
package gg
|
||||
|
||||
// Implementing the interface type
|
||||
// will call the function OnStart
|
||||
// when first appear on scene BEFORE
|
||||
// the OnUpdate.
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
GetLayer() Layer
|
||||
IsVisible() bool
|
||||
}
|
||||
|
||||
type Objecter interface {
|
||||
GetObject() *Object
|
||||
Input() chan *Context
|
||||
Starter
|
||||
Updater
|
||||
// The interface must
|
||||
// me implemented for all the
|
||||
// in-game logic objects.
|
||||
type Object interface {
|
||||
Drawer
|
||||
Deleter
|
||||
OnStart(Context)
|
||||
OnUpdate(Context)
|
||||
OnDelete(Context)
|
||||
GetTags(Context) TagMap
|
||||
}
|
||||
|
||||
// The type for embedding into engine-in types.
|
||||
type Object struct {
|
||||
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) Draw(c *Context) {}
|
||||
func (o *Object) Delete(c *Context) {}
|
||||
|
||||
|
|
124
objects.go
Normal file
124
objects.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package gg
|
||||
|
||||
type ObjectMap map[Object] struct{}
|
||||
|
||||
// The structure to store objects
|
||||
// with O(1) add, remove and search operations.
|
||||
type Objects struct {
|
||||
// The place to store all the objects.
|
||||
store []Object
|
||||
|
||||
// Find index by Object.
|
||||
searchMap map[Object] int
|
||||
|
||||
tagMap map[Tag] ObjectMap
|
||||
|
||||
// Available nil values in the store.
|
||||
freeIndexes []int
|
||||
}
|
||||
|
||||
func NewObjects() *Objects {
|
||||
ret := &Objects{}
|
||||
ret.searchMap = map[Object] int {}
|
||||
ret.tagMap = map[Tag]ObjectMap{}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (objects *Objects) has(object Object) bool {
|
||||
_, ok := objects.searchMap[object]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (objects *Objects) add(object Object) bool {
|
||||
if objects.has(object) {
|
||||
return false
|
||||
}
|
||||
if len(objects.freeIndexes) > 0 {
|
||||
freeIndex := objects.freeIndexes[0]
|
||||
objects.freeIndexes = objects.freeIndexes[1:]
|
||||
|
||||
objects.store[freeIndex] = object
|
||||
objects.searchMap[object] = freeIndex
|
||||
} else {
|
||||
objects.store = append(objects.store, object)
|
||||
objects.searchMap[object] = len(objects.store)-1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (objects *Objects) remove(object Object) bool {
|
||||
objectIndex, ok := objects.searchMap[object]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
objects.store[objectIndex] = nil
|
||||
objects.freeIndexes = append(
|
||||
objects.freeIndexes,
|
||||
objectIndex,
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (objects *Objects) FindByTags(
|
||||
t Tag, rest ...Tag,
|
||||
) []Object {
|
||||
tagMap := objects.tagMap
|
||||
objectMap := tagMap[t]
|
||||
retMap := map[Object]struct{}{}
|
||||
for object := range objectMap {
|
||||
retMap[object] = struct{}{}
|
||||
}
|
||||
|
||||
for _, tag := range rest {
|
||||
m, ok := tagMap[tag]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for object := range m {
|
||||
_, ok := m[object]
|
||||
if !ok {
|
||||
delete(retMap, object)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret := []Object{}
|
||||
for object := range retMap {
|
||||
ret = append(ret, object)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (objects *Objects) updateObjects(c Context){
|
||||
for _, object := range objects.store {
|
||||
if object == nil {
|
||||
continue
|
||||
}
|
||||
object.OnUpdate(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (objects *Objects) updateTags(c Context) {
|
||||
tagMap := objects.tagMap
|
||||
updateMap := make(map[Tag]ObjectMap, len(tagMap))
|
||||
|
||||
for _, object := range objects.store {
|
||||
tags := object.GetTags(c)
|
||||
for tag := range tags {
|
||||
tm, ok := updateMap[tag]
|
||||
if ok {
|
||||
tm[object] = struct{}{}
|
||||
} else {
|
||||
updateMap[tag] = ObjectMap{
|
||||
object: struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
objects.tagMap = updateMap
|
||||
}
|
||||
|
62
ox/asprite.go
Normal file
62
ox/asprite.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package ox
|
||||
|
||||
//import "time"
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/ax"
|
||||
|
||||
var _ = gg.Drawer(&AnimatedSprite{})
|
||||
// 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(
|
||||
animations ax.AnimationSet,
|
||||
timeBetweenFrames gg.Duration,
|
||||
) AnimatedSprite {
|
||||
ret := AnimatedSprite{}
|
||||
ret.animations = animations
|
||||
ret.timeBetweenFrames = timeBetweenFrames
|
||||
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 gg.Context) *gg.Drawing {
|
||||
as.duration += c.Engine().DrawDT()
|
||||
|
||||
frames := as.animations[as.animationId]
|
||||
fullTime := gg.Duration(len(frames)) * as.timeBetweenFrames
|
||||
if as.duration > fullTime {
|
||||
as.duration -= fullTime
|
||||
}
|
||||
|
||||
as.Images[0] = frames[
|
||||
(as.duration/as.timeBetweenFrames) %
|
||||
gg.Duration(len(frames)),
|
||||
]
|
||||
|
||||
return as.Sprite.Draw(c)
|
||||
}
|
||||
|
83
ox/camera.go
Normal file
83
ox/camera.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package ox
|
||||
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
|
||||
// Default camera implementation.
|
||||
var _ = gg.Camera(&Camera{})
|
||||
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 mx.Matrice
|
||||
}
|
||||
|
||||
// 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.IsDirty() {
|
||||
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().GetAbsWinSize()
|
||||
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 (camera *Camera) GetAbsMatrice(ctx gg.Context) mx.Matrice {
|
||||
if !camera.Transform.IsDirty() {
|
||||
return camera.absMatrice
|
||||
}
|
||||
m := camera.GetRealMatrice(ctx)
|
||||
m.Invert()
|
||||
|
||||
camera.absMatrice = m
|
||||
return m
|
||||
}
|
||||
|
||||
func (camera *Camera) GetAbsWinSize(
|
||||
ctx gg.Context,
|
||||
) mx.Vector {
|
||||
return ctx.Engine().GetRealWinSize().
|
||||
Div(camera.GetScale())
|
||||
}
|
||||
|
32
ox/circle.go
Normal file
32
ox/circle.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package ox
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
type Circle struct {
|
||||
ObjectImpl
|
||||
mx.Circle
|
||||
Visibility
|
||||
Colority
|
||||
Antialiasity
|
||||
gg.Layer
|
||||
}
|
||||
|
||||
func (circle *Circle) Draw(c gg.Context) gg.Drawing {
|
||||
center := circle.Center.
|
||||
Apply(c.Camera().GetRealMatrice(c))
|
||||
vector.DrawFilledCircle(
|
||||
c.Image(),
|
||||
float32(center.X), float32(center.Y),
|
||||
float32(circle.Radius),
|
||||
circle.Color,
|
||||
circle.Antialias,
|
||||
)
|
||||
return gg.Drawing{}
|
||||
}
|
||||
|
||||
|
56
ox/feats.go
Normal file
56
ox/feats.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package ox
|
||||
|
||||
import "surdeus.su/core/gg"
|
||||
|
||||
|
||||
type Drawity struct {
|
||||
ObjectImpl
|
||||
Visibility
|
||||
Colority
|
||||
Shaderity
|
||||
Floatity
|
||||
Layer
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func (v *Visibility) ToggleVisibility() bool {
|
||||
v.Visible = !v.Visible
|
||||
return v.IsVisible()
|
||||
}
|
||||
|
||||
// Feat to embed to make colorful objects.
|
||||
type Colority struct {
|
||||
Color gg.Color
|
||||
}
|
||||
|
||||
// The structure to embed into shaderable
|
||||
// objects.
|
||||
type Shaderity struct {
|
||||
gg.ShaderOptions
|
||||
}
|
||||
|
||||
func (s Shaderity) GetShaderOptions(
|
||||
) *gg.ShaderOptions {
|
||||
return &s.ShaderOptions
|
||||
}
|
||||
|
||||
type Floatity struct {
|
||||
Floating bool
|
||||
}
|
||||
func (s Floatity) IsFloating() bool {
|
||||
return s.Floating
|
||||
}
|
||||
|
||||
type Layer = gg.Layer
|
30
ox/object.go
Normal file
30
ox/object.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package ox
|
||||
|
||||
import "surdeus.su/core/gg"
|
||||
|
||||
type DrawerImpl struct{}
|
||||
func (o DrawerImpl) Draw(c gg.Context) *gg.Drawing {
|
||||
return nil
|
||||
}
|
||||
func (o DrawerImpl) GetLayer() gg.Layer {return 0}
|
||||
func (o DrawerImpl) IsVisible() bool {return false}
|
||||
|
||||
type BeherImpl struct{}
|
||||
func (o BeherImpl) OnStart(c gg.Context) {}
|
||||
func (o BeherImpl) OnUpdate(c gg.Context) {}
|
||||
func (o BeherImpl) OnDelete(c gg.Context) {}
|
||||
func (o BeherImpl) GetTags(
|
||||
c gg.Context,
|
||||
) gg.TagMap {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The standard empty implementation
|
||||
// of the Object interface to embed
|
||||
// so you do not need to implement everything.
|
||||
var _ = gg.Object(ObjectImpl{})
|
||||
type ObjectImpl struct {
|
||||
DrawerImpl
|
||||
BeherImpl
|
||||
}
|
||||
|
56
ox/polygon.go
Normal file
56
ox/polygon.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package ox
|
||||
|
||||
import (
|
||||
"surdeus.su/core/gg"
|
||||
"surdeus.su/core/gg/mx"
|
||||
)
|
||||
|
||||
// Grouped triangles type.
|
||||
type Polygon struct {
|
||||
Transform
|
||||
mx.Triangles
|
||||
}
|
||||
|
||||
func (p Polygon) GetContainedPoints(
|
||||
pts mx.Vectors,
|
||||
) (mx.Vectors) {
|
||||
return p.MakeTriangles().
|
||||
GetContainedPoints(pts)
|
||||
}
|
||||
|
||||
func (p Polygon) MakeTriangles() mx.Triangles {
|
||||
m := p.Transform.GetMatrice()
|
||||
ret := make(mx.Triangles, len(p.Triangles))
|
||||
for i, t := range p.Triangles {
|
||||
ret[i] = mx.Triangle{
|
||||
t[0].Apply(m),
|
||||
t[1].Apply(m),
|
||||
t[2].Apply(m),
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p Polygon) GetVertices() mx.Vectors {
|
||||
return p.MakeTriangles().GetVertices()
|
||||
}
|
||||
|
||||
func (p Polygon) GetEdges() mx.Lines {
|
||||
return p.MakeTriangles().GetEdges()
|
||||
}
|
||||
|
||||
var _ = gg.Drawer(&DrawablePolygon{})
|
||||
// Polygon that can be drawn.
|
||||
type DrawablePolygon struct {
|
||||
Polygon
|
||||
Drawity
|
||||
}
|
||||
|
||||
func (p *DrawablePolygon) Draw(c gg.Context) *gg.Drawing {
|
||||
tri := &DrawableTriangles{}
|
||||
tri.Triangles = p.MakeTriangles()
|
||||
tri.Drawity = p.Drawity
|
||||
return tri.Draw(c)
|
||||
}
|
||||
|
99
ox/rect.go
Normal file
99
ox/rect.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package ox
|
||||
|
||||
import (
|
||||
//"github.com/hajimehoshi/ebiten/v2"
|
||||
//"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
//"fmt"
|
||||
//"image"
|
||||
)
|
||||
import "surdeus.su/core/gg"
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
// The type describes rectangle geometry with
|
||||
// way to move, rotate and scale it.
|
||||
type Rectangle struct {
|
||||
Transform
|
||||
Width, Height mx.Float
|
||||
}
|
||||
|
||||
// Return points of vertices of the rectangle.
|
||||
func (r Rectangle) Vertices() mx.Vectors {
|
||||
t := r.Transform
|
||||
wh := mx.Vector{r.Width, r.Height}
|
||||
t.SetAround(t.GetAround().Mul(wh))
|
||||
m := t.GetMatrice()
|
||||
|
||||
p1 := mx.Vector{0, 0}.Apply(m)
|
||||
p2 := mx.Vector{wh.X, 0}.Apply(m)
|
||||
p3 := mx.Vector{wh.X, wh.Y}.Apply(m)
|
||||
p4 := mx.Vector{0, wh.Y}.Apply(m)
|
||||
|
||||
return mx.Vectors{p1, p2, p3, p4}
|
||||
}
|
||||
|
||||
func (r Rectangle) Edges() mx.Lines {
|
||||
vs := r.Vertices()
|
||||
return mx.Lines{
|
||||
mx.Line{vs[0], vs[1]},
|
||||
mx.Line{vs[1], vs[2]},
|
||||
mx.Line{vs[2], vs[3]},
|
||||
mx.Line{vs[3], vs[0]},
|
||||
}
|
||||
}
|
||||
|
||||
// Get 2 triangles that the rectangle consists of.
|
||||
func (r Rectangle) MakeTriangles() mx.Triangles {
|
||||
pts := r.Vertices()
|
||||
p1 := pts[0]
|
||||
p2 := pts[1]
|
||||
p3 := pts[2]
|
||||
p4 := pts[3]
|
||||
|
||||
return mx.Triangles{
|
||||
mx.Triangle{p1, p2, p3},
|
||||
mx.Triangle{p1, p4, p3},
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the rectangle contains the point.
|
||||
func (r Rectangle) ContainsPoint(pt mx.Vector) bool {
|
||||
return r.MakeTriangles().ContainsPoint(pt)
|
||||
}
|
||||
|
||||
// Get the points that are contained in the rectangle.
|
||||
func (r Rectangle) GetContainedPoints(
|
||||
pts mx.Vectors,
|
||||
) (mx.Vectors) {
|
||||
return r.MakeTriangles().GetContainedPoints(pts)
|
||||
}
|
||||
|
||||
// The type describes rectangle that can be drawn.
|
||||
type DrawableRectangle struct {
|
||||
Rectangle
|
||||
Drawity
|
||||
}
|
||||
|
||||
func (r *DrawableRectangle) Draw(c gg.Context) *gg.Drawing {
|
||||
tri := &DrawableTriangles{}
|
||||
tri.Drawity = r.Drawity
|
||||
tri.Triangles = r.MakeTriangles()
|
||||
return tri.Draw(c)
|
||||
|
||||
/*// Use the Color as base image if no is provided.
|
||||
//var did bool
|
||||
if r.Images[0] == nil {
|
||||
r.Images[0] = NewImage(1, 1)
|
||||
r.Images[0].Set(0, 0, r.Color)
|
||||
}
|
||||
|
||||
w, h := r.Images[0].Size()
|
||||
|
||||
// Drawing with shader.
|
||||
c.DrawRectShader(w, h, r.Shader, &ebiten.DrawRectShaderOptions{
|
||||
GeoM: m,
|
||||
Images: r.Images,
|
||||
Uniforms: r.Uniforms,
|
||||
})*/
|
||||
return nil
|
||||
}
|
||||
|
117
ox/sprite.go
Normal file
117
ox/sprite.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
package ox
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
import "surdeus.su/core/gg/mx"
|
||||
import "surdeus.su/core/gg"
|
||||
|
||||
var _ = gg.Drawer(&Sprite{})
|
||||
type Sprite struct {
|
||||
Transform
|
||||
Drawity
|
||||
}
|
||||
|
||||
var (
|
||||
//spritesOp DrawImageOptions
|
||||
)
|
||||
|
||||
func (s *Sprite) Draw(c gg.Context) *gg.Drawing {
|
||||
// Nothing to draw.
|
||||
if s.Images[0] == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
t := s.GetRectangleToDraw().Transform
|
||||
m := t.GetMatrice()
|
||||
if !s.IsFloating() {
|
||||
m.Concat(c.Camera().GetRealMatrice(c))
|
||||
}
|
||||
|
||||
// Drawing without shader.
|
||||
if s.Shader == nil {
|
||||
c.Image().DrawImage(s.Images[0], &ebiten.DrawImageOptions{
|
||||
GeoM: m,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
w, h := s.Images[0].Size()
|
||||
// Drawing with shader.
|
||||
c.Image().DrawRectShader(
|
||||
w, h,
|
||||
s.Shader,
|
||||
&ebiten.DrawRectShaderOptions{
|
||||
Images: s.Images,
|
||||
Uniforms: s.Uniforms,
|
||||
GeoM: m,
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the rectangle that contains the sprite to draw.
|
||||
func (s *Sprite) GetRectangleToDraw() Rectangle {
|
||||
if s.Images[0] == nil {
|
||||
return Rectangle{}
|
||||
}
|
||||
|
||||
w, h := s.Images[0].Size()
|
||||
t := s.Transform
|
||||
t.SetAround(
|
||||
t.GetAround().Mul(
|
||||
mx.Vector{mx.Float(w), mx.Float(h)},
|
||||
),
|
||||
)
|
||||
|
||||
return Rectangle{
|
||||
Transform: t,
|
||||
}
|
||||
}
|
||||
|
||||
// Return the rectangle that contains
|
||||
// the sprite in the engine.
|
||||
func (s *Sprite) GetRectangle() Rectangle {
|
||||
if s.Images[0] == nil {
|
||||
return Rectangle{
|
||||
Transform: s.Transform,
|
||||
}
|
||||
}
|
||||
|
||||
w, h := s.Images[0].Size()
|
||||
t := s.Transform
|
||||
|
||||
return Rectangle{
|
||||
Transform: t,
|
||||
Width: mx.Float(w),
|
||||
Height: mx.Float(h),
|
||||
}
|
||||
}
|
||||
|
||||
// Get triangles of the rectangle that contains the sprite
|
||||
// and has the same widght and height.
|
||||
func (s *Sprite) MakeTriangles() mx.Triangles {
|
||||
return s.GetRectangle().MakeTriangles()
|
||||
}
|
||||
|
||||
func (s *Sprite) Vertices() mx.Vectors {
|
||||
return s.GetRectangle().Vertices()
|
||||
}
|
||||
|
||||
func (s *Sprite) Edges() mx.Lines {
|
||||
return s.GetRectangle().Edges()
|
||||
}
|
||||
|
||||
func (s *Sprite) ContainsPoint(
|
||||
pt mx.Vector,
|
||||
) bool {
|
||||
return s.GetRectangle().ContainsPoint(pt)
|
||||
}
|
||||
|
||||
func (s *Sprite) GetContainedPoints(
|
||||
pts mx.Vectors,
|
||||
) mx.Vectors {
|
||||
return s.GetRectangle().GetContainedPoints(pts)
|
||||
}
|
33
ox/text.go
Normal file
33
ox/text.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package ox
|
||||
|
||||
//import "surdeus.su/core/gg/mx"
|
||||
import "surdeus.su/core/gg"
|
||||
import "github.com/hajimehoshi/ebiten/v2/text"
|
||||
import "github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
// The type implements basic drawable text.
|
||||
// (Now needs to implement rotation, scaling etc, cause now only position)
|
||||
type Text struct {
|
||||
Transform
|
||||
Drawity
|
||||
Face gg.Face
|
||||
Data string
|
||||
}
|
||||
|
||||
func (txt *Text) Draw(c gg.Context) *gg.Drawing {
|
||||
m := txt.Transform.GetMatrice()
|
||||
if !txt.IsFloating() {
|
||||
m.Concat(c.Camera().GetRealMatrice(c))
|
||||
}
|
||||
//x, y := txt.Position.XY()
|
||||
//text.Draw(c.Image)
|
||||
text.DrawWithOptions(
|
||||
c.Image(),
|
||||
txt.Data,
|
||||
txt.Face,
|
||||
&ebiten.DrawImageOptions{
|
||||
GeoM: m,
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
274
ox/transform.go
Normal file
274
ox/transform.go
Normal file
|
@ -0,0 +1,274 @@
|
|||
package ox
|
||||
|
||||
import "surdeus.su/core/gg/mx"
|
||||
|
||||
type Transformer interface {
|
||||
GetTransform() *Transform
|
||||
}
|
||||
|
||||
// The structure represents basic transformation
|
||||
// features: positioning, rotating and scaling
|
||||
// in more high level and more "GAME ENGINE" style.
|
||||
type Transform struct {
|
||||
// Absolute (if no parent) position and
|
||||
// the scale.
|
||||
position, scale mx.Vector
|
||||
// The object rotation in radians.
|
||||
rotation mx.Float
|
||||
// The not scaled offset vector from upper left corner
|
||||
// which the object should be rotated around.
|
||||
around mx.Vector
|
||||
|
||||
// If is not nil then the upper values will be relational to
|
||||
// the parent ones.
|
||||
parent *Transform
|
||||
|
||||
// Dirty is true if we anyhow changed matrix.
|
||||
dirty, parentDirty bool
|
||||
|
||||
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),
|
||||
}
|
||||
ret.dirty = true
|
||||
ret.parentDirty = true
|
||||
return ret
|
||||
}
|
||||
|
||||
// Returns whether it needs to be recalculated
|
||||
// or not.
|
||||
func (t *Transform) IsDirty() bool {
|
||||
return t.dirty || t.parentDirty
|
||||
}
|
||||
|
||||
// For implementing the Transformer on embedding.
|
||||
func (t *Transform) GetTransform() *Transform {
|
||||
return t
|
||||
}
|
||||
|
||||
// Set the absolute object position.
|
||||
func (t *Transform) SetPosition(position mx.Vector) *Transform {
|
||||
t.dirty = true
|
||||
t.parentDirty = true
|
||||
if t.parent == nil {
|
||||
t.position = position
|
||||
return t
|
||||
}
|
||||
|
||||
_, mi := t.parent.GetMatriceForParenting()
|
||||
t.position = position.Apply(mi)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Set the absolute object rotation.
|
||||
func (t *Transform) SetRotation(rotation mx.Float) *Transform {
|
||||
t.dirty = true
|
||||
t.parentDirty = true
|
||||
if t.parent == nil {
|
||||
t.rotation = rotation
|
||||
return t
|
||||
}
|
||||
t.rotation -= t.parent.GetRotation()
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Set the absolute object scale.
|
||||
func (t *Transform) SetScale(scale mx.Vector) *Transform {
|
||||
t.dirty = true
|
||||
t.parentDirty = true
|
||||
t.scale = scale
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Transform) AddScale(add mx.Vector) *Transform {
|
||||
t.dirty = true
|
||||
//t.parentDirty = true
|
||||
t.scale = t.scale.Add(add)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Transform) Scale(mul mx.Vector) *Transform {
|
||||
t.dirty = true
|
||||
t.scale = t.scale.Mul(mul)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Transform) SetAround(around mx.Vector) *Transform {
|
||||
t.dirty = true
|
||||
t.around = around
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Get the absolute representation of the transform.
|
||||
func (t *Transform) Abs() Transform {
|
||||
if t.parent == nil {
|
||||
return *t
|
||||
}
|
||||
|
||||
ret := Transform{}
|
||||
ret.position = t.GetPosition()
|
||||
ret.rotation = t.GetRotation()
|
||||
ret.scale = t.GetScale()
|
||||
ret.around = t.GetAround()
|
||||
ret.dirty = true
|
||||
ret.parentDirty = true
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (t *Transform) Rel() Transform {
|
||||
ret := *t
|
||||
ret.parent = nil
|
||||
return ret
|
||||
}
|
||||
|
||||
// Get the absolute object position.
|
||||
func (t *Transform) GetPosition() mx.Vector {
|
||||
if t.parent == nil {
|
||||
return t.position
|
||||
}
|
||||
pm, _ := t.parent.GetMatriceForParenting()
|
||||
return t.position.Apply(pm)
|
||||
}
|
||||
|
||||
// Move by the specified delta.
|
||||
func (t *Transform) Move(v mx.Vector) {
|
||||
t.SetPosition(t.GetPosition().Add(v))
|
||||
}
|
||||
|
||||
// Get the absolute object scale.
|
||||
func (t *Transform) GetScale() mx.Vector {
|
||||
return t.scale
|
||||
}
|
||||
|
||||
// Get the absolute object rotation.
|
||||
func (t *Transform) GetRotation() mx.Float {
|
||||
if t.parent == nil {
|
||||
return t.rotation
|
||||
}
|
||||
return t.rotation + t.parent.GetRotation()
|
||||
}
|
||||
|
||||
func (t *Transform) Rotate(rot mx.Float) *Transform {
|
||||
t.dirty = true
|
||||
t.parentDirty = true
|
||||
t.rotation += rot
|
||||
|
||||
return t
|
||||
}
|
||||
func (t *Transform) GetAround() mx.Vector {
|
||||
return t.around
|
||||
}
|
||||
|
||||
// Returns true if the object is connected
|
||||
// to some parent.
|
||||
func (t *Transform) IsConnected() bool {
|
||||
return t.parent != nil
|
||||
}
|
||||
|
||||
// Connect the object to another one making
|
||||
// it its parent.
|
||||
func (t *Transform) Connect(parent Transformer) *Transform {
|
||||
if parent == nil {
|
||||
return t
|
||||
}
|
||||
if t.parent != nil {
|
||||
t.Disconnect()
|
||||
}
|
||||
|
||||
position := t.GetPosition()
|
||||
rotation := t.GetRotation()
|
||||
|
||||
t.parent = parent.GetTransform()
|
||||
t.SetPosition(position)
|
||||
t.SetRotation(rotation)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Disconnect from the parent.
|
||||
func (t *Transform) Disconnect() *Transform {
|
||||
if t.parent == nil {
|
||||
return t
|
||||
}
|
||||
*t = t.Abs()
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Return the matrix and the inverted
|
||||
// one for parenting children.
|
||||
func (t *Transform) GetMatriceForParenting(
|
||||
) (mx.Matrice, mx.Matrice) {
|
||||
var m, mi mx.Matrice
|
||||
if t.parentDirty {
|
||||
|
||||
//m.Scale(t.scale.X, t.scale.Y)
|
||||
m.Rotate(t.rotation)
|
||||
m.Translate(t.position.X, t.position.Y)
|
||||
t.parentMatrice = m
|
||||
|
||||
mi = m
|
||||
mi.Invert()
|
||||
t.parentInverted = mi
|
||||
|
||||
t.parentDirty = false
|
||||
} else {
|
||||
m = t.parentMatrice
|
||||
mi = t.parentInverted
|
||||
}
|
||||
|
||||
if t.parent != nil {
|
||||
pm, pmi := t.parent.GetMatriceForParenting()
|
||||
m.Concat(pm)
|
||||
pmi.Concat(mi)
|
||||
mi = pmi
|
||||
}
|
||||
|
||||
return m, mi
|
||||
}
|
||||
|
||||
// Returns the GeoM with corresponding
|
||||
// to the transfrom transformation.
|
||||
func (t *Transform) GetMatrice() mx.Matrice {
|
||||
var m, pm mx.Matrice
|
||||
|
||||
|
||||
// Calculating only if we changed the structure anyhow.
|
||||
if t.dirty {
|
||||
// Scale first.
|
||||
m.Scale(t.scale.X, t.scale.Y)
|
||||
// Then move and rotate.
|
||||
m.Translate(
|
||||
-t.around.X * t.scale.X,
|
||||
-t.around.Y * t.scale.Y,
|
||||
)
|
||||
m.Rotate(t.rotation)
|
||||
// And finally move to the absolute position.
|
||||
m.Translate(t.position.X, t.position.Y)
|
||||
t.matrix = m
|
||||
|
||||
// Setting the flag so we d
|
||||
t.dirty = false
|
||||
} else {
|
||||
m = t.matrix
|
||||
}
|
||||
|
||||
if t.parent != nil {
|
||||
pm, _ = t.parent.GetMatriceForParenting()
|
||||
m.Concat(pm)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
34
ox/tri.go
Normal file
34
ox/tri.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package ox
|
||||
|
||||
import "surdeus.su/core/gg/mx"
|
||||
import "surdeus.su/core/gg"
|
||||
//import "github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
type DrawableTriangles struct {
|
||||
mx.Triangles
|
||||
Drawity
|
||||
}
|
||||
|
||||
func (r *DrawableTriangles) MakeVertices(
|
||||
c gg.Context,
|
||||
) gg.Vertices {
|
||||
m := c.Camera().GetRealMatrice(c)
|
||||
vs := make(gg.Vertices, len(r.Triangles) * 3)
|
||||
var buf gg.Vertice
|
||||
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
|
||||
}
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
func (r *DrawableTriangles) Draw(
|
||||
c gg.Context,
|
||||
) *gg.Drawing {
|
||||
return &gg.Drawing{
|
||||
Vertices: r.MakeVertices(c),
|
||||
}
|
||||
}
|
48
polygon.go
48
polygon.go
|
@ -1,48 +0,0 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
)
|
||||
|
||||
// Grouped triangles type.
|
||||
type Polygon struct {
|
||||
Object
|
||||
Transform
|
||||
Triangles
|
||||
}
|
||||
|
||||
func (p Polygon) ContainsPoint(pnt Point) bool {
|
||||
return p.MakeTriangles().ContainsPoint(pnt)
|
||||
}
|
||||
|
||||
// Polygon that can be drawn.
|
||||
type DrawablePolygon struct {
|
||||
Polygon
|
||||
|
||||
ShaderOptions
|
||||
Visibility
|
||||
Colority
|
||||
}
|
||||
|
||||
func (p Polygon) MakeTriangles() Triangles {
|
||||
m := p.Matrix()
|
||||
ret := make(Triangles, len(p.Triangles))
|
||||
for i, t := range p.Triangles {
|
||||
ret[i] = Triangle{
|
||||
t[0].Apply(m),
|
||||
t[1].Apply(m),
|
||||
t[2].Apply(m),
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *DrawablePolygon) Draw(c *Context) {
|
||||
(&DrawableTriangles{
|
||||
Visibility: p.Visibility,
|
||||
Colority: p.Colority,
|
||||
ShaderOptions: p.ShaderOptions,
|
||||
Triangles: p.MakeTriangles(),
|
||||
}).Draw(c)
|
||||
}
|
||||
|
106
rect.go
106
rect.go
|
@ -1,106 +0,0 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
//"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
//"fmt"
|
||||
//"image"
|
||||
)
|
||||
|
||||
// The type describes rectangle geometry.
|
||||
type Rectangle struct {
|
||||
Object
|
||||
Transform
|
||||
}
|
||||
|
||||
// The type describes rectangle that can be drawn.
|
||||
type DrawableRectangle struct {
|
||||
Rectangle
|
||||
ShaderOptions
|
||||
|
||||
// Solid color of the rectangle.
|
||||
// Will be ignored if the Shader
|
||||
// field is not nil.
|
||||
Colority
|
||||
|
||||
// Should be draw or not.
|
||||
Visibility
|
||||
}
|
||||
|
||||
// Return points of vertices of the rectangle.
|
||||
func (r Rectangle) Vertices() Points {
|
||||
m := r.Matrix()
|
||||
p1 := V(0, 0).Apply(m)
|
||||
p2 := V(1, 0).Apply(m)
|
||||
p3 := V(1, 1).Apply(m)
|
||||
p4 := V(0, 1).Apply(m)
|
||||
return Points{p1, p2, p3, p4}
|
||||
}
|
||||
|
||||
func (r Rectangle) Edges() Edges {
|
||||
vs := r.Vertices()
|
||||
return LineSegments{
|
||||
LineSegment{vs[0], vs[1]},
|
||||
LineSegment{vs[1], vs[2]},
|
||||
LineSegment{vs[2], vs[3]},
|
||||
LineSegment{vs[3], vs[0]},
|
||||
}
|
||||
}
|
||||
|
||||
// Get 2 triangles that the rectangle consists of.
|
||||
func (r Rectangle) Triangles() Triangles {
|
||||
pts := r.Vertices()
|
||||
p1 := pts[0]
|
||||
p2 := pts[1]
|
||||
p3 := pts[2]
|
||||
p4 := pts[3]
|
||||
|
||||
return Triangles{
|
||||
Triangle{p1, p2, p3},
|
||||
Triangle{p1, p4, p3},
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the rectangle contains the point.
|
||||
func (r Rectangle) ContainsPoint(p Point) bool {
|
||||
return r.Triangles().ContainsPoint(p)
|
||||
}
|
||||
|
||||
// Check whether the drawable rectangle should be drawn.
|
||||
func (r *DrawableRectangle) IsVisible() bool {
|
||||
return r.Visible
|
||||
}
|
||||
|
||||
func (r *DrawableRectangle) Draw(c *Context) {
|
||||
m := r.Matrix()
|
||||
rm := c.Camera.RealMatrix()
|
||||
m.Concat(rm)
|
||||
// Draw solid color if no shader.
|
||||
if r.Shader == nil {
|
||||
img := NewImage(1, 1)
|
||||
img.Set(0, 0, r.Color)
|
||||
|
||||
c.DrawImage(img, &ebiten.DrawImageOptions{
|
||||
GeoM: m,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Use the Color as base image if no is provided.
|
||||
//var did bool
|
||||
if r.Images[0] == nil {
|
||||
r.Images[0] = NewImage(1, 1)
|
||||
r.Images[0].Set(0, 0, r.Color)
|
||||
}
|
||||
|
||||
w, h := r.Images[0].Size()
|
||||
|
||||
// Drawing with shader.
|
||||
c.DrawRectShader(w, h, r.Shader, &ebiten.DrawRectShaderOptions{
|
||||
GeoM: m,
|
||||
Images: r.Images,
|
||||
Uniforms: r.Uniforms,
|
||||
})
|
||||
}
|
||||
|
39
shader.go
39
shader.go
|
@ -5,51 +5,24 @@ import (
|
|||
//"fmt"
|
||||
)
|
||||
|
||||
type Shader = ebiten.Shader
|
||||
type ShaderOptions struct {
|
||||
Shader *Shader
|
||||
Uniforms map[string] any
|
||||
Images [4]*Image
|
||||
}
|
||||
|
||||
type Shader = ebiten.Shader
|
||||
|
||||
var (
|
||||
// The shader is for example only.
|
||||
SolidWhiteColorShader = MustNewShader([]byte(`
|
||||
package main
|
||||
|
||||
var Random float
|
||||
//var Random float
|
||||
|
||||
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
|
||||
//ts := imageSrcTextureSize()
|
||||
|
||||
//_, size := imageSrcRegionOnTexture()
|
||||
/*return vec4(
|
||||
position.y/size.y,
|
||||
position.y/size.y,
|
||||
position.y/size.y,
|
||||
position.y/size.y,
|
||||
)*/
|
||||
/*py := int(position.y / size.y) % 5
|
||||
px := int(position.x / size.x) % 5
|
||||
if py >= 1 && px >= 1 {
|
||||
return vec4(
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
}*/
|
||||
|
||||
/*ret := vec4(
|
||||
0,
|
||||
sin(position.x),
|
||||
sin(position.y),
|
||||
1,
|
||||
)
|
||||
|
||||
return imageSrc0UnsafeAt(texCoord) * (ret)*/
|
||||
return imageSrc0UnsafeAt(texCoord) *
|
||||
vec4(1, 1, Random, Random)
|
||||
func Fragment(dst vec4, src vec2, _ vec4) vec4 {
|
||||
c := imageSrc0UnsafeAt(src)
|
||||
return c * sin(dst.x)
|
||||
}
|
||||
`))
|
||||
)
|
||||
|
|
1
short.go
Normal file
1
short.go
Normal file
|
@ -0,0 +1 @@
|
|||
package gg
|
66
sprite.go
66
sprite.go
|
@ -1,66 +0,0 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type Sprite struct {
|
||||
Object
|
||||
Transform
|
||||
ShaderOptions
|
||||
Floating bool
|
||||
Visibility
|
||||
}
|
||||
|
||||
func (s *Sprite) Draw(c *Context) {
|
||||
// Nothing to draw.
|
||||
if s.Images[0] == nil {
|
||||
return
|
||||
}
|
||||
|
||||
t := s.Rectangle().Transform
|
||||
m := t.Matrix()
|
||||
if !s.Floating {
|
||||
m.Concat(c.Camera.RealMatrix())
|
||||
}
|
||||
|
||||
// Drawing without shader.
|
||||
if s.Shader == nil {
|
||||
c.DrawImage(s.Images[0], &ebiten.DrawImageOptions{
|
||||
GeoM: m,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w, h := s.Images[0].Size()
|
||||
// Drawing with shader.
|
||||
c.DrawRectShader(w, h, s.Shader, &ebiten.DrawRectShaderOptions{
|
||||
Images: s.Images,
|
||||
Uniforms: s.Uniforms,
|
||||
GeoM: m,
|
||||
})
|
||||
}
|
||||
|
||||
// Return the rectangle that contains the sprite.
|
||||
func (s *Sprite) Rectangle() Rectangle {
|
||||
if s.Images[0] == nil {
|
||||
panic("trying to get rectangle for nil image pointer")
|
||||
}
|
||||
|
||||
w, h := s.Images[0].Size()
|
||||
t := s.Transform
|
||||
t.Around.X *= Float(w)
|
||||
t.Around.Y *= Float(h)
|
||||
|
||||
return Rectangle{
|
||||
Transform: t,
|
||||
}
|
||||
}
|
||||
|
||||
// Get triangles of the rectangle that contains the sprite
|
||||
// and has the same widght and height.
|
||||
func (s *Sprite) Triangles() Triangles {
|
||||
return s.Rectangle().Triangles()
|
||||
}
|
||||
|
16
tag.go
Normal file
16
tag.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package gg
|
||||
|
||||
type Tag int64
|
||||
type TagMap map[Tag] struct{}
|
||||
|
||||
const (
|
||||
_ Tag = iota
|
||||
TagUI
|
||||
|
||||
// The constant is used
|
||||
// to make after it custom tags
|
||||
// so they do not cross with the builtins
|
||||
// like "TagUI".
|
||||
TagLast
|
||||
)
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
version: 3
|
||||
tasks:
|
||||
btest:
|
||||
cmds:
|
||||
- go build ./cmd/test/
|
51
text.go
Normal file
51
text.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
//"strings"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/opentype"
|
||||
"io"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
//type FontOptions = font.Options
|
||||
var (
|
||||
FontHintingNone = font.HintingNone
|
||||
)
|
||||
type Face = font.Face
|
||||
|
||||
type FontTTF = opentype.Font
|
||||
type FaceOptionsTTF = opentype.FaceOptions
|
||||
type FaceTTF = opentype.Face
|
||||
|
||||
func MakeFaceFromTTF(
|
||||
src io.ReaderAt,
|
||||
opts *FaceOptionsTTF,
|
||||
) (Face, error) {
|
||||
fnt, err := ParseFontTTF(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
face, err := NewFaceTTF(fnt, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return face, nil
|
||||
}
|
||||
|
||||
func ParseFontTTF(
|
||||
src io.ReaderAt,
|
||||
) (*FontTTF, error) {
|
||||
return opentype.ParseReaderAt(src)
|
||||
}
|
||||
|
||||
func NewFaceTTF(
|
||||
fnt *FontTTF,
|
||||
opts *FaceOptionsTTF,
|
||||
) (Face, error) {
|
||||
return opentype.NewFace(fnt, opts)
|
||||
}
|
||||
|
||||
|
7
time.go
7
time.go
|
@ -7,3 +7,10 @@ import (
|
|||
type Time = time.Time
|
||||
type Duration = time.Duration
|
||||
|
||||
const (
|
||||
ZeroDuration Duration = 0
|
||||
Microsecond = time.Microsecond
|
||||
Millisecond = time.Millisecond
|
||||
Second = time.Second
|
||||
)
|
||||
|
||||
|
|
153
transform.go
153
transform.go
|
@ -1,153 +0,0 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
//"github.com/hajimehoshi/ebiten/v2"
|
||||
//"math"
|
||||
)
|
||||
|
||||
type Transformer interface {
|
||||
GetTransform() Transform
|
||||
}
|
||||
|
||||
// The structure represents basic transformation
|
||||
// features: positioning, rotating and scaling.
|
||||
type Transform struct {
|
||||
// Absolute (if no parent) position and
|
||||
// the scale.
|
||||
Position, Scale Vector
|
||||
// The object rotation in radians.
|
||||
Rotation Float
|
||||
// The not scaled offset vector from upper left corner
|
||||
// which the object should be rotated around.
|
||||
Around Vector
|
||||
// If is not nil then the upper values will be relational to
|
||||
// the parent ones.
|
||||
Parent Transformer
|
||||
}
|
||||
|
||||
func (t Transform) GetTransform() Transform {
|
||||
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
|
||||
}
|
||||
|
||||
func (t *Transform) SetAbsPosition(absPosition Vector) {
|
||||
if t.Parent == nil {
|
||||
t.Position = absPosition
|
||||
return
|
||||
}
|
||||
m := t.ParentMatrix()
|
||||
m.Invert()
|
||||
t.Position = absPosition.Apply(m)
|
||||
}
|
||||
|
||||
// Get the absolute representation of the transform.
|
||||
func (t Transform) Abs() Transform {
|
||||
if t.Parent == nil {
|
||||
return t
|
||||
}
|
||||
|
||||
ret := T()
|
||||
ret.Position = t.AbsPosition()
|
||||
ret.Rotation = t.AbsRotation()
|
||||
ret.Scale = t.AbsScale()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (t Transform) AbsPosition() Vector {
|
||||
if t.Parent == nil {
|
||||
return t.Position
|
||||
}
|
||||
return t.Position.Apply(t.ParentMatrix())
|
||||
}
|
||||
|
||||
func (t Transform) AbsScale() Vector {
|
||||
if t.Parent == nil {
|
||||
return t.Scale
|
||||
}
|
||||
return V2(1)
|
||||
}
|
||||
|
||||
func (t Transform) AbsRotation() Float {
|
||||
if t.Parent == nil {
|
||||
return t.Rotation
|
||||
}
|
||||
return t.Rotation + t.Parent.GetTransform().AbsRotation()
|
||||
}
|
||||
|
||||
func (t *Transform) SetAbsRotation(rot Float) {
|
||||
if t.Parent == nil {
|
||||
t.Rotation = rot
|
||||
}
|
||||
t.Rotation -= t.Parent.GetTransform().AbsRotation()
|
||||
}
|
||||
|
||||
func (t *Transform) Connected() bool {
|
||||
return t.Parent != nil
|
||||
}
|
||||
|
||||
func (t *Transform) Connect(parent Transformer) {
|
||||
absPosition := t.AbsPosition()
|
||||
absRotation := t.AbsRotation()
|
||||
t.Parent = parent
|
||||
t.SetAbsPosition(absPosition)
|
||||
t.SetAbsRotation(absRotation)
|
||||
}
|
||||
|
||||
func (t *Transform) Disconnect() {
|
||||
if t.Parent == nil {
|
||||
return
|
||||
}
|
||||
*t = t.Abs()
|
||||
}
|
||||
|
||||
func (t Transform) ParentMatrix() Matrix {
|
||||
g := Matrix{}
|
||||
if t.Parent == nil {
|
||||
return g
|
||||
}
|
||||
|
||||
p := t.Parent.GetTransform()
|
||||
g = p.ParentMatrix()
|
||||
|
||||
g.Scale(p.Scale.X, p.Scale.Y)
|
||||
g.Rotate(p.Rotation)
|
||||
g.Translate(p.Position.X, p.Position.Y)
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
// Returns the GeoM with corresponding
|
||||
// to the transfrom transformation.
|
||||
func (t Transform)Matrix() Matrix {
|
||||
g := Matrix{}
|
||||
|
||||
// Scale first.
|
||||
g.Scale(t.Scale.X, t.Scale.Y)
|
||||
|
||||
// Then move and rotate.
|
||||
g.Translate(
|
||||
-t.Around.X * t.Scale.X,
|
||||
-t.Around.Y * t.Scale.Y,
|
||||
)
|
||||
g.Rotate(t.Rotation)
|
||||
|
||||
// And finally move to the absolute position.
|
||||
g.Translate(t.Position.X, t.Position.Y)
|
||||
|
||||
m := t.ParentMatrix()
|
||||
g.Concat(m)
|
||||
|
||||
return g
|
||||
}
|
||||
|
134
triangle.go
134
triangle.go
|
@ -1,134 +0,0 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
"math"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
// Ebitens vector in better abstractions like Vectors.
|
||||
type Vertex struct {
|
||||
Dst Vector
|
||||
Src Vector
|
||||
Colority
|
||||
}
|
||||
|
||||
type Triangle [3]Vector
|
||||
type Triangles []Triangle
|
||||
type DrawableTriangles struct {
|
||||
Triangles
|
||||
Visibility
|
||||
Colority
|
||||
ShaderOptions
|
||||
ebiten.DrawTrianglesOptions
|
||||
}
|
||||
|
||||
func (v Vertex) Ebiten() ebiten.Vertex {
|
||||
return ebiten.Vertex {
|
||||
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 := LineSegment{t[0], t[1]}.LenSqr()
|
||||
l2 := LineSegment{t[1], t[2]}.LenSqr()
|
||||
l3 := LineSegment{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) 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 (ts Triangles) ContainsPoint(p Point) bool {
|
||||
for _, t := range ts {
|
||||
if t.ContainsPoint(p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *DrawableTriangles) Draw(c *Context) {
|
||||
m := c.Camera.RealMatrix()
|
||||
|
||||
// Draw solid color if no shader.
|
||||
if r.Shader == nil {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
is := make([]uint16, len(r.Triangles) * 3)
|
||||
for i := 0 ; i < len(is) ; i++ {
|
||||
is[i] = uint16(i)
|
||||
}
|
||||
|
||||
img := NewImage(1, 1)
|
||||
img.Set(0, 0, r.Color)
|
||||
|
||||
c.DrawTriangles(vs, is, img, &r.DrawTrianglesOptions)
|
||||
return
|
||||
}
|
||||
|
||||
// Use the Color as base image if no is provided.
|
||||
/*if r.Images[0] == nil {
|
||||
r.Images[0] = NewImage(1, 1)
|
||||
r.Images[0].Set(0, 0, r.Color)
|
||||
}
|
||||
|
||||
w, h := r.Images[0].Size()
|
||||
|
||||
// Drawing with shader.
|
||||
opts := &ebiten.DrawRectShaderOptions{
|
||||
GeoM: m,
|
||||
Images: r.Images,
|
||||
Uniforms: r.Uniforms,
|
||||
}
|
||||
i.DrawRectShader(w, h, r.Shader, opts)*/
|
||||
}
|
||||
|
107
vector.go
107
vector.go
|
@ -1,109 +1,4 @@
|
|||
package gg
|
||||
|
||||
import (
|
||||
//"github.com/hajimehoshi/ebiten/v2"
|
||||
"math"
|
||||
)
|
||||
|
||||
var (
|
||||
ZV = V2(0)
|
||||
)
|
||||
|
||||
type Vector struct {
|
||||
X, Y Float
|
||||
}
|
||||
type Point = Vector
|
||||
//type Vertex = Vector
|
||||
|
||||
type Vectors []Vector
|
||||
type Points []Point
|
||||
|
||||
func V(x, y Float) Vector {
|
||||
return Vector{x, y}
|
||||
}
|
||||
|
||||
func V2(v Float) Vector {
|
||||
return V(v, v)
|
||||
}
|
||||
|
||||
func (v Vector) Div(o Vector) Vector {
|
||||
return V(
|
||||
v.X / o.X,
|
||||
v.Y / o.Y,
|
||||
)
|
||||
}
|
||||
|
||||
func (v Vector) Scale(o Vector) Vector {
|
||||
return V(
|
||||
v.X * o.X,
|
||||
v.Y * o.Y,
|
||||
)
|
||||
}
|
||||
|
||||
func (v Vector) Eq(o Vector) bool {
|
||||
return v.X == o.X && v.Y == o.Y
|
||||
}
|
||||
|
||||
// Returns the vector with the matrix applied
|
||||
func (v Vector) Apply(m Matrix) Vector {
|
||||
x, y := m.Apply(v.X, v.Y)
|
||||
return Vector{x, y}
|
||||
}
|
||||
|
||||
// Adds the vector to other one returning the result.
|
||||
func (v Vector) Add(a ...Vector) Vector {
|
||||
for _, r := range a {
|
||||
v.X += r.X
|
||||
v.Y += r.Y
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Returns the subtraction of all the vectors from the current one.
|
||||
func (v Vector) Sub(s ...Vector) Vector {
|
||||
for _, r := range s {
|
||||
v.X -= r.X
|
||||
v.Y -= r.Y
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Returns the negative version of the vector.
|
||||
func (v Vector) Neg() Vector {
|
||||
return Vector{
|
||||
-v.X,
|
||||
-v.Y,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the vector rotated by "a" angle in radians.
|
||||
func (v Vector) Rotate(a Float) Vector {
|
||||
m := Matrix{}
|
||||
m.Rotate(a)
|
||||
return v.Apply(m)
|
||||
}
|
||||
|
||||
// Returns the normalized vector.
|
||||
func (v Vector) Norm() Vector {
|
||||
l := math.Sqrt(v.X*v.X + v.Y*v.Y)
|
||||
return V(v.X / l, v.Y / l)
|
||||
}
|
||||
|
||||
func (pts Points) Contained(c PointContainer) Points {
|
||||
ret := make([]Point, 0, len(pts))
|
||||
|
||||
for _, pt := range pts {
|
||||
if c.ContainsPoint(pt) {
|
||||
ret = append(ret, pt)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (pts Points) Len() int {
|
||||
return len(pts)
|
||||
}
|
||||
//import "surdeus.su/core/gg/mx"
|
||||
|
||||
|
|
37
vertice.go
Normal file
37
vertice.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package gg
|
||||
|
||||
import "github.com/hajimehoshi/ebiten/v2"
|
||||
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
|
||||
}
|
||||
|
||||
// Convert the vertice into the Ebiten's type.
|
||||
func (v Vertice) ToAPI() EVertice {
|
||||
r, g, b, a := v.RGBA()
|
||||
return EVertice {
|
||||
DstX: float32(v.Dst.X),
|
||||
DstY: float32(v.Dst.Y),
|
||||
SrcX: float32(v.Src.X),
|
||||
SrcY: float32(v.Src.Y),
|
||||
ColorR: float32(r)/(float32(MaxColorValue)),
|
||||
ColorG: float32(g)/(float32(MaxColorValue)),
|
||||
ColorB: float32(b)/(float32(MaxColorValue)),
|
||||
ColorA: float32(a)/(float32(MaxColorValue)),
|
||||
}
|
||||
}
|
||||
|
||||
type Vertices []Vertice
|
||||
func (vs Vertices) ToAPI() []EVertice {
|
||||
ret := make([]EVertice, len(vs))
|
||||
for i, v := range vs {
|
||||
ret[i] = v.ToAPI()
|
||||
}
|
||||
return ret
|
||||
}
|
Loading…
Reference in a new issue