abolish transform
This commit is contained in:
parent
02d91f9d23
commit
024cf2cafe
13 changed files with 102 additions and 96 deletions
|
@ -17,12 +17,14 @@ func init() {
|
||||||
// Actor handles basic movement.
|
// Actor handles basic movement.
|
||||||
type Actor struct {
|
type Actor struct {
|
||||||
CollisionDomain string // id of component to look for colliders inside of
|
CollisionDomain string // id of component to look for colliders inside of
|
||||||
Pos, Size Int3
|
Pos, Size Int3 // in voxels; multiply by game.VoxelScale for regular Euclidean space
|
||||||
xRem, yRem, zRem float64
|
|
||||||
|
|
||||||
|
rem Float3
|
||||||
game *Game
|
game *Game
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CollidesAt runs a collision test of the actor, supposing the actor is at a
|
||||||
|
// given position (not necessarily a.Pos).
|
||||||
func (a *Actor) CollidesAt(p Int3) bool {
|
func (a *Actor) CollidesAt(p Int3) bool {
|
||||||
bounds := Box{Min: p, Max: p.Add(a.Size)}
|
bounds := Box{Min: p, Max: p.Add(a.Size)}
|
||||||
for c := range a.game.Query(a.CollisionDomain, ColliderType) {
|
for c := range a.game.Query(a.CollisionDomain, ColliderType) {
|
||||||
|
@ -33,13 +35,17 @@ func (a *Actor) CollidesAt(p Int3) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MoveX moves the actor x units in world space. It takes Game.VoxelScale into
|
||||||
|
// account (so MoveX(x) moves the actor x/VoxelScale.X voxel units). onCollide
|
||||||
|
// is called if a collision occurs, and the actor wil be in the colliding
|
||||||
|
// position during the call.
|
||||||
func (a *Actor) MoveX(x float64, onCollide func()) {
|
func (a *Actor) MoveX(x float64, onCollide func()) {
|
||||||
a.xRem += x
|
a.rem.X += x / a.game.VoxelScale.X
|
||||||
move := int(a.xRem + 0.5) // Note: math.Round can lead to vibration
|
move := int(a.rem.X + 0.5) // Note: math.Round can lead to vibration
|
||||||
if move == 0 {
|
if move == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a.xRem -= float64(move)
|
a.rem.X -= float64(move)
|
||||||
sign := sign(move)
|
sign := sign(move)
|
||||||
for move != 0 {
|
for move != 0 {
|
||||||
a.Pos.X += sign
|
a.Pos.X += sign
|
||||||
|
@ -51,18 +57,19 @@ func (a *Actor) MoveX(x float64, onCollide func()) {
|
||||||
onCollide()
|
onCollide()
|
||||||
}
|
}
|
||||||
a.Pos.X -= sign
|
a.Pos.X -= sign
|
||||||
a.xRem = 0
|
a.rem.X = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MoveY is like MoveX but in the Y dimension. See MoveX for more information.
|
||||||
func (a *Actor) MoveY(y float64, onCollide func()) {
|
func (a *Actor) MoveY(y float64, onCollide func()) {
|
||||||
a.yRem += y
|
a.rem.Y += y / a.game.VoxelScale.Y
|
||||||
move := int(a.yRem + 0.5)
|
move := int(a.rem.Y + 0.5)
|
||||||
if move == 0 {
|
if move == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a.yRem -= float64(move)
|
a.rem.Y -= float64(move)
|
||||||
sign := sign(move)
|
sign := sign(move)
|
||||||
for move != 0 {
|
for move != 0 {
|
||||||
a.Pos.Y += sign
|
a.Pos.Y += sign
|
||||||
|
@ -74,18 +81,19 @@ func (a *Actor) MoveY(y float64, onCollide func()) {
|
||||||
onCollide()
|
onCollide()
|
||||||
}
|
}
|
||||||
a.Pos.Y -= sign
|
a.Pos.Y -= sign
|
||||||
a.yRem = 0
|
a.rem.Y = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MoveZ is like MoveX but in the Y dimension. See MoveX for more information.
|
||||||
func (a *Actor) MoveZ(z float64, onCollide func()) {
|
func (a *Actor) MoveZ(z float64, onCollide func()) {
|
||||||
a.zRem += z
|
a.rem.Z += z / a.game.VoxelScale.Z
|
||||||
move := int(a.zRem + 0.5)
|
move := int(a.rem.Z + 0.5)
|
||||||
if move == 0 {
|
if move == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a.zRem -= float64(move)
|
a.rem.Z -= float64(move)
|
||||||
sign := sign(move)
|
sign := sign(move)
|
||||||
for move != 0 {
|
for move != 0 {
|
||||||
a.Pos.Z += sign
|
a.Pos.Z += sign
|
||||||
|
@ -97,11 +105,12 @@ func (a *Actor) MoveZ(z float64, onCollide func()) {
|
||||||
onCollide()
|
onCollide()
|
||||||
}
|
}
|
||||||
a.Pos.Z -= sign
|
a.Pos.Z -= sign
|
||||||
a.zRem = 0
|
a.rem.Z = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare stores a reference to the game.
|
||||||
func (a *Actor) Prepare(g *Game) error {
|
func (a *Actor) Prepare(g *Game) error {
|
||||||
a.game = g
|
a.game = g
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (b *Billboard) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
|
||||||
// Scan returns a slice containing Src.
|
// Scan returns a slice containing Src.
|
||||||
func (b *Billboard) Scan() []interface{} { return []interface{}{&b.Src} }
|
func (b *Billboard) Scan() []interface{} { return []interface{}{&b.Src} }
|
||||||
|
|
||||||
func (b *Billboard) Transform(pt Transform) (tf Transform) {
|
func (b *Billboard) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
tf.Opts.GeoM.Translate(cfloat(b.Pos))
|
opts.GeoM.Translate(cfloat(b.Pos))
|
||||||
return tf.Concat(pt)
|
return opts
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package engine
|
||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"image"
|
"image"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure Camera satisfies interfaces.
|
// Ensure Camera satisfies interfaces.
|
||||||
|
@ -87,11 +89,10 @@ func (c *Camera) Prepare(game *Game) error {
|
||||||
func (c *Camera) Scan() []interface{} { return []interface{}{c.Child} }
|
func (c *Camera) Scan() []interface{} { return []interface{}{c.Child} }
|
||||||
|
|
||||||
// Transform returns the camera transform.
|
// Transform returns the camera transform.
|
||||||
func (c *Camera) Transform(pt Transform) (tf Transform) {
|
func (c *Camera) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
tf.Projection = c.Projection
|
opts.GeoM.Translate(cfloat(c.Centre.Mul(-1)))
|
||||||
tf.Opts.GeoM.Translate(cfloat(c.Centre.Mul(-1)))
|
opts.GeoM.Scale(c.Zoom, c.Zoom)
|
||||||
tf.Opts.GeoM.Scale(c.Zoom, c.Zoom)
|
opts.GeoM.Rotate(c.Rotation)
|
||||||
tf.Opts.GeoM.Rotate(c.Rotation)
|
opts.GeoM.Translate(cfloat(c.game.ScreenSize.Div(2)))
|
||||||
tf.Opts.GeoM.Translate(cfloat(c.game.ScreenSize.Div(2)))
|
return opts
|
||||||
return tf.Concat(pt)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ type Game struct {
|
||||||
Hidden
|
Hidden
|
||||||
ScreenSize image.Point
|
ScreenSize image.Point
|
||||||
Root interface{} // typically a *Scene or SceneRef though
|
Root interface{} // typically a *Scene or SceneRef though
|
||||||
|
Projection IntProjection
|
||||||
VoxelScale Float3
|
VoxelScale Float3
|
||||||
|
|
||||||
dbmu sync.RWMutex
|
dbmu sync.RWMutex
|
||||||
|
@ -62,7 +63,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
// accum memoises the results for each component.
|
// accum memoises the results for each component.
|
||||||
type state struct {
|
type state struct {
|
||||||
hidden bool
|
hidden bool
|
||||||
transform Transform
|
opts ebiten.DrawImageOptions
|
||||||
}
|
}
|
||||||
accum := map[interface{}]state{
|
accum := map[interface{}]state{
|
||||||
g: {hidden: false},
|
g: {hidden: false},
|
||||||
|
@ -96,9 +97,9 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
accum[p] = state{hidden: true}
|
accum[p] = state{hidden: true}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// p is not hidden, so compute its cumulative transform.
|
// p is not hidden, so compute its cumulative opts.
|
||||||
if tf, ok := p.(Transformer); ok {
|
if tf, ok := p.(Transformer); ok {
|
||||||
st.transform = tf.Transform(st.transform)
|
st.opts = ConcatOpts(tf.Transform(), st.opts)
|
||||||
}
|
}
|
||||||
accum[p] = st
|
accum[p] = st
|
||||||
}
|
}
|
||||||
|
@ -107,7 +108,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
if st.hidden {
|
if st.hidden {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
d.Draw(screen, &st.transform.Opts)
|
d.Draw(screen, &st.opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,6 +266,10 @@ func postorderWalk(component, parent interface{}, visit func(component, parent i
|
||||||
// builds the component databases and then calls Prepare on every Preparer.
|
// builds the component databases and then calls Prepare on every Preparer.
|
||||||
// LoadAndPrepare must be called before any calls to Component or Query.
|
// LoadAndPrepare must be called before any calls to Component or Query.
|
||||||
func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
||||||
|
if g.VoxelScale == (Float3{}) {
|
||||||
|
g.VoxelScale = Float3{1, 1, 1}
|
||||||
|
}
|
||||||
|
|
||||||
// Load all the Loaders.
|
// Load all the Loaders.
|
||||||
startLoad := time.Now()
|
startLoad := time.Now()
|
||||||
if err := PreorderWalk(g, func(c, _ interface{}) error {
|
if err := PreorderWalk(g, func(c, _ interface{}) error {
|
||||||
|
@ -440,3 +445,17 @@ func (d drawList) Less(i, j int) bool {
|
||||||
|
|
||||||
func (d drawList) Len() int { return len(d) }
|
func (d drawList) Len() int { return len(d) }
|
||||||
func (d drawList) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
func (d drawList) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||||
|
|
||||||
|
// ConcatOpts returns the combined options (as though a was applied and then
|
||||||
|
// b).
|
||||||
|
func ConcatOpts(a, b ebiten.DrawImageOptions) ebiten.DrawImageOptions {
|
||||||
|
a.ColorM.Concat(b.ColorM)
|
||||||
|
a.GeoM.Concat(b.GeoM)
|
||||||
|
if b.CompositeMode != 0 {
|
||||||
|
a.CompositeMode = b.CompositeMode
|
||||||
|
}
|
||||||
|
if b.Filter != 0 {
|
||||||
|
a.Filter = b.Filter
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
|
@ -105,17 +105,11 @@ type Saver interface {
|
||||||
Save() error
|
Save() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transformer components can provide a transform to apply to themselves and any
|
// Transformer components can provide draw options to apply to themselves and
|
||||||
// child components, based on the cumulative parent transform. An
|
// any child components. The opts passed to Draw of a component c will be the
|
||||||
// example implementation:
|
// cumulative opts of all parents of c plus the value returned from c.Transform.
|
||||||
//
|
|
||||||
// func (f Foo) Transform(pt Transform) Transform {
|
|
||||||
// var tf Transform
|
|
||||||
// tf.Opts.GeoM.Translate(-2, 3) // or your own transform
|
|
||||||
// return tf.Concat(pt)
|
|
||||||
// }
|
|
||||||
type Transformer interface {
|
type Transformer interface {
|
||||||
Transform(Transform) Transform
|
Transform() ebiten.DrawImageOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updater components can update themselves. Update is called repeatedly. Each
|
// Updater components can update themselves. Update is called repeatedly. Each
|
||||||
|
|
|
@ -3,6 +3,8 @@ package engine
|
||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
|
@ -39,8 +41,8 @@ func (p *Parallax) Prepare(game *Game) error {
|
||||||
func (p *Parallax) Scan() []interface{} { return []interface{}{p.Child} }
|
func (p *Parallax) Scan() []interface{} { return []interface{}{p.Child} }
|
||||||
|
|
||||||
// Transform returns a GeoM translation of Factor * camera.Centre.
|
// Transform returns a GeoM translation of Factor * camera.Centre.
|
||||||
func (p *Parallax) Transform(pt Transform) (tf Transform) {
|
func (p *Parallax) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
x, y := cfloat(p.camera.Centre)
|
x, y := cfloat(p.camera.Centre)
|
||||||
tf.Opts.GeoM.Translate(x*p.Factor, y*p.Factor)
|
opts.GeoM.Translate(x*p.Factor, y*p.Factor)
|
||||||
return tf.Concat(pt)
|
return opts
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,9 @@ func (m *PrismMap) Prepare(*Game) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PrismMap) Transform(pt Transform) (tf Transform) {
|
func (m *PrismMap) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
tf.Opts.GeoM.Translate(cfloat(m.DrawOffset))
|
opts.GeoM.Translate(cfloat(m.DrawOffset))
|
||||||
return tf.Concat(pt)
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
type Prism struct {
|
type Prism struct {
|
||||||
|
@ -78,9 +78,9 @@ func (p *Prism) DrawOrder() (int, int) {
|
||||||
dot(p.pos.XY(), p.pm.DrawOrderBias)
|
dot(p.pos.XY(), p.pm.DrawOrderBias)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prism) Transform(pt Transform) (tf Transform) {
|
func (p *Prism) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
tf.Opts.GeoM.Translate(cfloat(
|
opts.GeoM.Translate(cfloat(
|
||||||
p.pm.PosToDraw.Apply(p.pos),
|
p.pm.PosToDraw.Apply(p.pos),
|
||||||
))
|
))
|
||||||
return tf.Concat(pt)
|
return opts
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,12 @@ package engine
|
||||||
|
|
||||||
import "image"
|
import "image"
|
||||||
|
|
||||||
|
// IntProjection holds an integer projection definition.
|
||||||
|
// It is designed for projecting Z onto X and Y with integer fractions as would
|
||||||
|
// be used in e.g. a diametric projection (IntProjection{X:0, Y:-2}).
|
||||||
type IntProjection image.Point
|
type IntProjection image.Point
|
||||||
|
|
||||||
// Project performs an integer parallel projection of a 3D coordinate into 2D.
|
// 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.
|
// 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.
|
// Otherwise, x projects to x + z/π.X and y projects to y + z/π.Y.
|
||||||
func (π IntProjection) Project(p Int3) image.Point {
|
func (π IntProjection) Project(p Int3) image.Point {
|
||||||
|
|
|
@ -56,12 +56,15 @@ func (s *Sprite) SetAnim(a *Anim) {
|
||||||
s.anim = a
|
s.anim = a
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform returns a translation by the DrawOffset and the iso-projected Pos.
|
// Transform returns a translation by the DrawOffset and Actor.Pos projected
|
||||||
func (s *Sprite) Transform(pt Transform) (tf Transform) {
|
func (s *Sprite) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
tf.Opts.GeoM.Translate(cfloat(
|
opts.GeoM.Translate(cfloat(
|
||||||
pt.Projection.Project(s.Actor.Pos).Add(s.DrawOffset),
|
// Reaching into Actor for a reference to Game so I don't have to
|
||||||
|
// implement Prepare in this file, but writing this long comment
|
||||||
|
// providing exposition...
|
||||||
|
s.Actor.game.Projection.Project(s.Actor.Pos).Add(s.DrawOffset),
|
||||||
))
|
))
|
||||||
return tf.Concat(pt)
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates the Sprite's anim. anim can change a bit so we don't tell Game
|
// Update updates the Sprite's anim. anim can change a bit so we don't tell Game
|
||||||
|
|
|
@ -110,9 +110,9 @@ func (t *Tilemap) Scan() []interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform returns a translation by t.Offset.
|
// Transform returns a translation by t.Offset.
|
||||||
func (t *Tilemap) Transform(pt Transform) (tf Transform) {
|
func (t *Tilemap) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
tf.Opts.GeoM.Translate(cfloat(t.Offset))
|
opts.GeoM.Translate(cfloat(t.Offset))
|
||||||
return tf.Concat(pt)
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// TileAt returns the tile present at the given world coordinate.
|
// TileAt returns the tile present at the given world coordinate.
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package engine
|
|
||||||
|
|
||||||
import "github.com/hajimehoshi/ebiten/v2"
|
|
||||||
|
|
||||||
// Transform is a bucket of things that affect drawing.
|
|
||||||
type Transform struct {
|
|
||||||
// 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.
|
|
||||||
Projection IntProjection
|
|
||||||
|
|
||||||
// Opts contains the 2D geometry matrix, the colour matrix, filter mode, and
|
|
||||||
// composition mode.
|
|
||||||
Opts ebiten.DrawImageOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concat returns the combined transform (a transform equivalent to applying t
|
|
||||||
// and then u).
|
|
||||||
func (t Transform) Concat(u Transform) Transform {
|
|
||||||
if u.Projection != (IntProjection{}) {
|
|
||||||
t.Projection = u.Projection
|
|
||||||
}
|
|
||||||
t.Opts.ColorM.Concat(u.Opts.ColorM)
|
|
||||||
t.Opts.GeoM.Concat(u.Opts.GeoM)
|
|
||||||
if u.Opts.CompositeMode != 0 {
|
|
||||||
t.Opts.CompositeMode = u.Opts.CompositeMode
|
|
||||||
}
|
|
||||||
if u.Opts.Filter != 0 {
|
|
||||||
t.Opts.Filter = u.Opts.Filter
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
|
@ -80,9 +80,9 @@ func (w *Wall) Prepare(*Game) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform returns a GeoM translation by Offset.
|
// Transform returns a GeoM translation by Offset.
|
||||||
func (w *Wall) Transform(pt Transform) (tf Transform) {
|
func (w *Wall) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
tf.Opts.GeoM.Translate(cfloat(w.Offset))
|
opts.GeoM.Translate(cfloat(w.Offset))
|
||||||
return tf.Concat(pt)
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// WallUnit is a unit in a wall. Unlike a tile in a tilemap, WallUnit is
|
// WallUnit is a unit in a wall. Unlike a tile in a tilemap, WallUnit is
|
||||||
|
@ -105,9 +105,9 @@ func (u *WallUnit) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
|
||||||
// Scan returns the Tile.
|
// Scan returns the Tile.
|
||||||
func (u *WallUnit) Scan() []interface{} { return []interface{}{u.Tile} }
|
func (u *WallUnit) Scan() []interface{} { return []interface{}{u.Tile} }
|
||||||
|
|
||||||
func (u *WallUnit) Transform(pt Transform) (tf Transform) {
|
func (u *WallUnit) Transform() (opts ebiten.DrawImageOptions) {
|
||||||
tf.Opts.GeoM.Translate(cfloat(
|
opts.GeoM.Translate(cfloat(
|
||||||
cmul(u.pos, u.wall.UnitSize).Add(u.wall.UnitOffset),
|
cmul(u.pos, u.wall.UnitSize).Add(u.wall.UnitOffset),
|
||||||
))
|
))
|
||||||
return tf.Concat(pt)
|
return opts
|
||||||
}
|
}
|
||||||
|
|
8
main.go
8
main.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"image/color"
|
"image/color"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"log"
|
"log"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
@ -51,6 +52,13 @@ func main() {
|
||||||
|
|
||||||
g := &engine.Game{
|
g := &engine.Game{
|
||||||
ScreenSize: image.Pt(320, 240),
|
ScreenSize: image.Pt(320, 240),
|
||||||
|
Projection: engine.IntProjection{X: 0, Y: 1},
|
||||||
|
VoxelScale: engine.Float3{
|
||||||
|
// Each voxel counts for this much Eucliden space:
|
||||||
|
X: 1,
|
||||||
|
Y: 1,
|
||||||
|
Z: math.Sqrt(3),
|
||||||
|
},
|
||||||
Root: &engine.Scene{
|
Root: &engine.Scene{
|
||||||
ID: "root",
|
ID: "root",
|
||||||
Components: []interface{}{
|
Components: []interface{}{
|
||||||
|
|
Loading…
Reference in a new issue