projection.go, delete crap

This commit is contained in:
Josh Deprez 2021-09-07 13:14:42 +10:00
parent 86d076c89f
commit 1e029903a6
10 changed files with 44 additions and 47 deletions

Binary file not shown.

View file

@ -38,21 +38,21 @@ func (b Box) Size() Point3 {
// Back returns an image.Rectangle representing the back of the box, using
// the given projection π.
func (b Box) Back(π image.Point) image.Rectangle {
func (b Box) Back(π IntProjection) image.Rectangle {
b.Max.Z = b.Min.Z
return image.Rectangle{
Min: b.Min.IsoProject(π),
Max: b.Max.IsoProject(π),
Min: π.Project(b.Min),
Max: π.Project(b.Max),
}
}
// Front returns an image.Rectangle representing the front of the box, using
// the given projection π.
func (b Box) Front(π image.Point) image.Rectangle {
func (b Box) Front(π IntProjection) image.Rectangle {
b.Min.Z = b.Max.Z
return image.Rectangle{
Min: b.Min.IsoProject(π),
Max: b.Max.IsoProject(π),
Min: π.Project(b.Min),
Max: π.Project(b.Max),
}
}

View file

@ -25,10 +25,10 @@ type Camera struct {
// Camera controls
// These directly manipulate the camera. If you want to restrict the camera
// view area to the child's bounding rectangle, use PointAt.
Centre image.Point // world coordinates
Rotation float64 // radians
Zoom float64 // unitless
IsoProjection image.Point
Centre image.Point // world coordinates
Rotation float64 // radians
Zoom float64 // unitless
Projection IntProjection
game *Game
}
@ -39,7 +39,7 @@ func (c *Camera) PointAt(centre Point3, zoom float64) {
// Special sauce: if Child has a BoundingRect, make some adjustments
bnd, ok := c.Child.(Bounder)
if !ok {
c.Centre = centre.IsoProject(c.IsoProjection)
c.Centre = c.Projection.Project(centre)
c.Zoom = zoom
return
}
@ -61,7 +61,7 @@ func (c *Camera) PointAt(centre Point3, zoom float64) {
// Camera frame currently Rectangle{ centre ± (screen/(2*zoom)) }.
sw2, sh2 := cfloat(c.game.ScreenSize.Div(2))
swz, shz := int(sw2/zoom), int(sh2/zoom)
cent := centre.IsoProject(c.IsoProjection)
cent := c.Projection.Project(centre)
if cent.X-swz < br.Min.X {
cent.X = br.Min.X + swz
}
@ -88,7 +88,7 @@ func (c *Camera) Scan() []interface{} { return []interface{}{c.Child} }
// Transform returns the camera transform.
func (c *Camera) Transform(pt Transform) (tf Transform) {
tf.IsoProjection = c.IsoProjection
tf.Projection = c.Projection
tf.Opts.GeoM.Translate(cfloat(c.Centre.Mul(-1)))
tf.Opts.GeoM.Scale(c.Zoom, c.Zoom)
tf.Opts.GeoM.Rotate(c.Rotation)

View file

@ -1,24 +1,29 @@
package engine
var (
// Oblique projections
CabinetProjection = ParallelProjection{0.5, 0.5}
CavalierProjection = ParallelProjection{1, 1}
import "image"
// Axonometric projections
ElevationProjection = ParallelProjection{0, 0}
DimetricProjection = ParallelProjection{0, 0.5}
HexPrismProjection = ParallelProjection{0, 0.577350269189626} // 1 ÷ √3
IsometricProjection = ParallelProjection{0, 0.707106781186548} // 1 ÷ √2
TrimetricProjection = ParallelProjection{0, 1}
)
type IntProjection image.Point
type ParallelProjection struct {
ZX, ZY float64
}
func (π ParallelProjection) Project(p Point3) (px, py float64) {
px = float64(p.X) + π.ZX*float64(p.Z)
py = float64(p.Y) + π.ZY*float64(p.Z)
return px, py
// Project performs an integer parallel projection of a 3D coordinate into 2D.
//
// If π.X = 0, the x returned is p.X; similarly for π.Y and y.
// Otherwise, x projects to x + z/π.X and y projects to y + z/π.Y.
func (π IntProjection) Project(p Point3) image.Point {
/*
I'm using the π character because I'm a maths wanker.
Dividing is used because there's little reason for an isometric
projection in a game to exaggerate the Z position.
Integers are used to preserve that "pixel perfect" calculation in case
you are making the next Celeste.
*/
q := p.XY()
if π.X != 0 {
q.X += p.Z / π.X
}
if π.Y != 0 {
q.Y += p.Z / π.Y
}
return q
}

View file

@ -59,7 +59,7 @@ func (s *Sprite) SetAnim(a *Anim) {
// Transform returns a translation by the DrawOffset and the iso-projected Pos.
func (s *Sprite) Transform(pt Transform) (tf Transform) {
tf.Opts.GeoM.Translate(cfloat(
s.Actor.Pos.IsoProject(pt.IsoProjection).Add(s.DrawOffset),
pt.Projection.Project(s.Actor.Pos).Add(s.DrawOffset),
))
return tf.Concat(pt)
}

View file

@ -1,17 +1,13 @@
package engine
import (
"image"
"github.com/hajimehoshi/ebiten/v2"
)
import "github.com/hajimehoshi/ebiten/v2"
// Transform is a bucket of things that affect drawing.
type Transform struct {
// IsoProjection is used by isometric 3D components to project their
// Projection is used by isometric 3D components to project their
// coordinates into 2D. There's usually only one component in the tree that
// sets this field, but it would apply to all descendants.
IsoProjection image.Point
Projection IntProjection
// Opts contains the 2D geometry matrix, the colour matrix, filter mode, and
// composition mode.
@ -21,8 +17,8 @@ type Transform struct {
// Concat returns the combined transform (a transform equivalent to applying t
// and then u).
func (t Transform) Concat(u Transform) Transform {
if u.IsoProjection != (image.Point{}) {
t.IsoProjection = u.IsoProjection
if u.Projection != (IntProjection{}) {
t.Projection = u.Projection
}
t.Opts.ColorM.Concat(u.Opts.ColorM)
t.Opts.GeoM.Concat(u.Opts.GeoM)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -57,12 +57,8 @@ func main() {
&engine.Camera{
ID: "game_camera",
Child: lev1,
// Each step in Z becomes -½ step in X plus ½ step in Y:
IsoProjection: image.Pt(-2, 2),
// Each step in Z becomes ½ step in Y:
//IsoProjection: image.Pt(0, 2),
// Each step in Z becomes a step in Y:
//IsoProjection: image.Pt(0, 1),
Projection: engine.IntProjection{X: 0, Y: 1},
},
&engine.DebugToast{ID: "toast", Pos: image.Pt(0, 15)},
engine.PerfDisplay{},