You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
5.2 KiB
255 lines
5.2 KiB
package main |
|
|
|
import ( |
|
"embed" |
|
_ "image/png" |
|
"log" |
|
"math" |
|
|
|
"github.com/hajimehoshi/ebiten/v2/inpututil" |
|
|
|
"github.com/hajimehoshi/ebiten/v2" |
|
"github.com/kvartborg/vector" |
|
"github.com/solarlune/tetra3d" |
|
) |
|
|
|
const ( |
|
X = iota |
|
Y |
|
Z |
|
) |
|
|
|
const ( |
|
defaultW = 320 |
|
defaultH = 240 |
|
) |
|
|
|
var boxBrown = tetra3d.NewColor(66/255.0, 40/255.0, 1/255.0, 255/255.0) |
|
|
|
//go:embed asset |
|
var assetFS embed.FS |
|
|
|
type Game struct { |
|
GameScene *tetra3d.Scene |
|
Camera *tetra3d.Camera |
|
|
|
CameraTilt float64 |
|
CameraRotate float64 |
|
|
|
NativeResolution bool |
|
|
|
boxes []*tetra3d.Model |
|
boxVelocities []vector.Vector |
|
|
|
PrevMousePosition vector.Vector |
|
|
|
w, h int |
|
} |
|
|
|
func NewGame() *Game { |
|
g := &Game{} |
|
|
|
data, err := assetFS.ReadFile("asset/shapes.gltf") |
|
if err != nil { |
|
panic(err) |
|
} |
|
|
|
library, err := tetra3d.LoadGLTFData(data, nil) |
|
if err != nil { |
|
panic(err) |
|
} |
|
|
|
g.GameScene = library.FindScene("Scene") |
|
|
|
//g.GameScene = tetra3d.NewScene("Scene") |
|
|
|
g.GameScene.FogMode = tetra3d.FogOff |
|
|
|
log.Println(g.GameScene.Root.WorldPosition()) |
|
|
|
// Tetra uses OpenGL's coordinate system (+X = Right, +Y = Up, +Z = Back), |
|
// in comparison to Blender's coordinate system (+X = Right, +Y = Forward, |
|
// +Z = Up). |
|
|
|
g.SetSize(defaultW, defaultH) |
|
|
|
const mapSize = 100 |
|
for y := 0.0; y < mapSize; y++ { |
|
for x := 0.0; x < mapSize; x++ { |
|
plane := tetra3d.NewCube() |
|
plane.ApplyMatrix(tetra3d.NewMatrix4Scale(5, 0.5, 5)) |
|
plane.SetVertexColor(tetra3d.Color{0, 0, 255, 255}) |
|
planeModel := tetra3d.NewModel(plane, "plane") |
|
|
|
g.GameScene.Root.AddChildren(planeModel) |
|
|
|
//planeModel.Rotate(0, 1, 0, math.Pi) |
|
|
|
planeModel.SetLocalPosition(vector.Vector{x * 10, -1.5, y * 10}) |
|
} |
|
} |
|
|
|
return g |
|
} |
|
|
|
func (g *Game) updateCamera() { |
|
pos := vector.Vector{0, 2, 2} |
|
if g.Camera == nil { |
|
g.Camera = tetra3d.NewCamera(g.w, g.h) |
|
} else { |
|
pos = g.Camera.LocalPosition() |
|
g.Camera.Resize(g.w, g.h) |
|
} |
|
g.Camera.SetLocalPosition(pos) |
|
} |
|
|
|
func (g *Game) Update() error { |
|
pos := g.Camera.LocalPosition() |
|
pos[X] += 0.005 |
|
g.Camera.SetLocalPosition(pos) |
|
|
|
/*x, y := ebiten.CursorPosition() |
|
if x < 0 { |
|
x = 0 |
|
} else if x > g.w { |
|
x = g.w |
|
} |
|
if y < 0 { |
|
y = 0 |
|
} else if y > g.h { |
|
y = g.h |
|
} |
|
|
|
pctX := float64(x) / float64(g.w) |
|
pctY := float64(y) / float64(g.h) |
|
if pctX < 0 { |
|
pctX = 0 |
|
} else if pctX > 1 { |
|
pctX = 1 |
|
} |
|
if pctY < 0 { |
|
pctY = 0 |
|
} else if pctY > 1 { |
|
pctY = 1 |
|
}*/ |
|
|
|
// Rotating the camera with the mouse |
|
|
|
// Rotate and tilt the camera according to mouse movements |
|
mx, my := ebiten.CursorPosition() |
|
|
|
mv := vector.Vector{float64(mx), float64(my)} |
|
|
|
diff := mv.Sub(g.PrevMousePosition) |
|
|
|
g.CameraTilt -= diff[1] * 0.005 |
|
g.CameraRotate -= diff[0] * 0.005 |
|
|
|
g.CameraTilt = math.Max(math.Min(g.CameraTilt, math.Pi/2-0.1), -math.Pi/2+0.1) |
|
|
|
tilt := tetra3d.NewMatrix4Rotate(1, 0, 0, g.CameraTilt) |
|
rotate := tetra3d.NewMatrix4Rotate(0, 1, 0, g.CameraRotate) |
|
|
|
// Order of this is important - tilt * rotate works, rotate * tilt does not, lol |
|
g.Camera.SetLocalRotation(tilt.Mult(rotate)) |
|
|
|
g.PrevMousePosition = mv.Clone() |
|
|
|
//localRot := tetra3d.NewMatrix4Rotate(1, 1, 1, math.Pi/4-(math.Pi*pctX)) |
|
//g.Camera.SetLocalRotation(localRot) |
|
//g.Camera.Rotate(1, 0, 0, math.Pi/4-(math.Pi*pctY)) |
|
//localRot = localRot.Rotated(0, 1, 0, math.Pi/4-(math.Pi*pctX)) |
|
|
|
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { |
|
g.fireBox() |
|
} |
|
|
|
for i, box := range g.boxes { |
|
position := box.LocalPosition() |
|
//log.Printf("%d %+v", i, position) |
|
if position[Y] <= -0.5 { |
|
// Hit ground. |
|
continue |
|
} |
|
|
|
pos := box.LocalPosition() |
|
box.SetLocalPosition(pos.Add(g.boxVelocities[i])) |
|
if g.boxVelocities[i][Y] > -0.1 { |
|
g.boxVelocities[i][Y] -= 0.001 |
|
} |
|
} |
|
|
|
// TODO rotate Y |
|
//_ = pctY |
|
|
|
//log.Println(x, y, g.w, g.h, pctX, pctY) |
|
|
|
return nil |
|
} |
|
|
|
func (g *Game) Draw(screen *ebiten.Image) { |
|
g.Camera.Clear() |
|
g.Camera.RenderNodes(g.GameScene, g.GameScene.Root) |
|
screen.DrawImage(g.Camera.ColorTexture, nil) |
|
|
|
//g.Camera.DrawDebugWireframe(screen, g.GameScene.Root, color.Black) |
|
g.Camera.DrawDebugText(screen, 1) |
|
} |
|
|
|
func (g *Game) Layout(w, h int) (int, int) { |
|
if g.NativeResolution && (w != g.w || h != g.h) { |
|
g.SetSize(w, h) |
|
} |
|
return g.w, g.h |
|
} |
|
|
|
func (g *Game) SetSize(w, h int) { |
|
g.w, g.h = w, h |
|
g.updateCamera() |
|
} |
|
|
|
func (g *Game) fireBox() { |
|
mesh := tetra3d.NewCube() |
|
mesh.SetVertexColor(*boxBrown) |
|
mesh.ApplyMatrix(tetra3d.NewMatrix4Scale(0.5, 0.5, 0.5)) |
|
|
|
box := tetra3d.NewModel(mesh, "Cube") |
|
|
|
box.SetLocalPosition(g.Camera.LocalPosition()) |
|
|
|
//cameraPos := g.Camera.LocalPosition() |
|
cameraRotation := g.Camera.LocalRotation() |
|
|
|
scale := 0.2 |
|
velocity := vector.Vector{ |
|
-cameraRotation[2][0] * scale, |
|
-cameraRotation[2][1] * scale, |
|
-cameraRotation[2][2] * scale, |
|
} |
|
velocity[Y] += 0.1 |
|
//log.Printf("%+v", velocity) |
|
|
|
g.boxes = append(g.boxes, box) |
|
g.boxVelocities = append(g.boxVelocities, velocity) |
|
|
|
g.GameScene.Root.AddChildren(box) |
|
} |
|
|
|
func main() { |
|
ebiten.SetWindowTitle("Brown Box Earth Simulator") |
|
ebiten.SetWindowResizable(true) |
|
ebiten.SetMaxTPS(144) |
|
ebiten.SetRunnableOnUnfocused(true) // Note - this currently does nothing in ebiten |
|
ebiten.SetWindowClosingHandled(false) // TODO |
|
ebiten.SetFPSMode(ebiten.FPSModeVsyncOn) |
|
ebiten.SetCursorMode(ebiten.CursorModeCaptured) |
|
|
|
g := NewGame() |
|
|
|
parseFlags(g) |
|
|
|
if err := ebiten.RunGame(g); err != nil { |
|
panic(err) |
|
} |
|
|
|
}
|
|
|