Created
July 29, 2022 22:36
-
-
Save SolarLune/1d9928648ad17161aa2e58cf5a226f5b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"fmt" | |
"image/png" | |
"math" | |
"os" | |
"runtime/pprof" | |
"time" | |
_ "embed" | |
"github.com/kvartborg/vector" | |
"github.com/solarlune/tetra3d" | |
"github.com/solarlune/tetra3d/colors" | |
"github.com/hajimehoshi/ebiten/v2" | |
"github.com/hajimehoshi/ebiten/v2/inpututil" | |
) | |
//go:embed lighting.gltf | |
var gltfData []byte | |
type Game struct { | |
Width, Height int | |
Library *tetra3d.Library | |
Scene *tetra3d.Scene | |
Camera *tetra3d.Camera | |
CameraTilt float64 | |
CameraRotate float64 | |
DrawDebugText bool | |
DrawDebugDepth bool | |
PrevMousePosition vector.Vector | |
Time float64 | |
} | |
func NewGame() *Game { | |
game := &Game{ | |
Width: 796, | |
Height: 448, | |
PrevMousePosition: vector.Vector{}, | |
} | |
game.Init() | |
return game | |
} | |
func (g *Game) Init() { | |
opt := tetra3d.DefaultGLTFLoadOptions() | |
opt.CameraWidth = g.Width | |
opt.CameraHeight = g.Height | |
library, err := tetra3d.LoadGLTFData(gltfData, opt) | |
if err != nil { | |
panic(err) | |
} | |
g.Library = library | |
g.Scene = library.Scenes[0] | |
g.Camera = tetra3d.NewCamera(g.Width, g.Height) | |
g.Camera.SetLocalPosition(vector.Vector{0, 2, 15}) | |
g.Scene.Root.AddChildren(g.Camera) | |
light := tetra3d.NewPointLight("camera light", 1, 1, 1, 2) | |
light.Distance = 10 | |
light.Move(0, 1, -2) | |
light.On = false | |
g.Camera.AddChildren(light) | |
// g.Scene.FogMode = tetra3d.FogMultiply | |
ebiten.SetCursorMode(ebiten.CursorModeCaptured) | |
} | |
func (g *Game) Update() error { | |
g.Time += 1.0 / 60.0 | |
var err error | |
moveSpd := 0.05 | |
if ebiten.IsKeyPressed(ebiten.KeyEscape) { | |
err = errors.New("quit") | |
} | |
spin := g.Scene.Root.Get("Spin").(*tetra3d.Model) | |
spin.Rotate(0, 1, 0, 0.025) | |
light := g.Scene.Root.Get("Point light").(*tetra3d.PointLight) | |
light.AnimationPlayer().Play(g.Library.Animations["LightAction"]) | |
light.AnimationPlayer().Update(1.0 / 60.0) | |
// g.Scene.Root.Get("plane").Rotate(1, 0, 0, 0.04) | |
// light := g.Scene.Root.Get("third point light") | |
// light.Move(math.Sin(g.Time*math.Pi)*0.1, 0, math.Cos(g.Time*math.Pi*0.19)*0.03) | |
// Moving the Camera | |
// We use Camera.Rotation.Forward().Invert() because the camera looks down -Z (so its forward vector is inverted) | |
forward := g.Camera.LocalRotation().Forward().Invert() | |
right := g.Camera.LocalRotation().Right() | |
pos := g.Camera.LocalPosition() | |
// g.Scene.Root.Get("point light").(*tetra3d.PointLight).Distance = 10 + (math.Sin(g.Time*math.Pi) * 5) | |
if ebiten.IsKeyPressed(ebiten.KeyW) { | |
pos = pos.Add(forward.Scale(moveSpd)) | |
} | |
if ebiten.IsKeyPressed(ebiten.KeyD) { | |
pos = pos.Add(right.Scale(moveSpd)) | |
} | |
if ebiten.IsKeyPressed(ebiten.KeyS) { | |
pos = pos.Add(forward.Scale(-moveSpd)) | |
} | |
if ebiten.IsKeyPressed(ebiten.KeyA) { | |
pos = pos.Add(right.Scale(-moveSpd)) | |
} | |
if ebiten.IsKeyPressed(ebiten.KeySpace) { | |
pos[1] += moveSpd | |
} | |
if ebiten.IsKeyPressed(ebiten.KeyControl) { | |
pos[1] -= moveSpd | |
} | |
g.Camera.SetLocalPosition(pos) | |
if inpututil.IsKeyJustPressed(ebiten.KeyF4) { | |
ebiten.SetFullscreen(!ebiten.IsFullscreen()) | |
} | |
armature := g.Scene.Root.Get("Armature") | |
if ebiten.IsKeyPressed(ebiten.KeyLeft) { | |
armature.Move(-0.1, 0, 0) | |
} else if ebiten.IsKeyPressed(ebiten.KeyRight) { | |
armature.Move(0.1, 0, 0) | |
} | |
player := armature.AnimationPlayer() | |
player.Play(g.Library.Animations["ArmatureAction"]) | |
player.Update(1.0 / 60.0) | |
// 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() | |
if inpututil.IsKeyJustPressed(ebiten.KeyF12) { | |
f, err := os.Create("screenshot" + time.Now().Format("2006-01-02 15:04:05") + ".png") | |
if err != nil { | |
fmt.Println(err) | |
} | |
defer f.Close() | |
png.Encode(f, g.Camera.ColorTexture()) | |
} | |
if ebiten.IsKeyPressed(ebiten.KeyR) { | |
g.Init() | |
} | |
if inpututil.IsKeyJustPressed(ebiten.KeyP) { | |
g.StartProfiling() | |
} | |
if inpututil.IsKeyJustPressed(ebiten.KeyF1) { | |
g.DrawDebugText = !g.DrawDebugText | |
} | |
if inpututil.IsKeyJustPressed(ebiten.KeyF5) { | |
g.DrawDebugDepth = !g.DrawDebugDepth | |
} | |
if inpututil.IsKeyJustPressed(ebiten.Key1) { | |
g.Scene.World.LightingOn = !g.Scene.World.LightingOn | |
} | |
if inpututil.IsKeyJustPressed(ebiten.Key2) { | |
pointLight := g.Camera.Get("camera light").(*tetra3d.PointLight) | |
pointLight.On = !pointLight.On | |
} | |
return err | |
} | |
func (g *Game) Draw(screen *ebiten.Image) { | |
// Clear, but with a color - we can use the world lighting color for this. | |
screen.Fill(g.Scene.World.ClearColor.ToRGBA64()) | |
// Clear the Camera | |
g.Camera.Clear() | |
// Render the scene | |
g.Camera.RenderNodes(g.Scene, g.Scene.Root) | |
// We rescale the depth or color textures here just in case we render at a different resolution than the window's; this isn't necessary, | |
// we could just draw the images straight. | |
opt := &ebiten.DrawImageOptions{} | |
w, h := g.Camera.ColorTexture().Size() | |
opt.GeoM.Scale(float64(g.Width)/float64(w), float64(g.Height)/float64(h)) | |
if g.DrawDebugDepth { | |
screen.DrawImage(g.Camera.DepthTexture(), opt) | |
} else { | |
screen.DrawImage(g.Camera.ColorTexture(), opt) | |
} | |
if g.DrawDebugText { | |
g.Camera.DrawDebugRenderInfo(screen, 1, colors.White()) | |
txt := "F1 to toggle this text\nWASD: Move, Mouse: Look\nThis example simply shows dynamic vertex-based lighting.\nThere are six lights in this scene:\nan ambient light, three point lights, \na single directional (sun) light,\nand one more point light parented to the camera.\n1 Key: Toggle all lighting\n2 Key: Toggle camera light\nF5: Toggle depth debug view\nF4: Toggle fullscreen\nESC: Quit" | |
g.Camera.DebugDrawText(screen, txt, 0, 150, 1, colors.Red()) | |
} | |
} | |
func (g *Game) StartProfiling() { | |
outFile, err := os.Create("./cpu.pprof") | |
if err != nil { | |
fmt.Println(err.Error()) | |
return | |
} | |
fmt.Println("Beginning CPU profiling...") | |
pprof.StartCPUProfile(outFile) | |
go func() { | |
time.Sleep(2 * time.Second) | |
pprof.StopCPUProfile() | |
fmt.Println("CPU profiling finished.") | |
}() | |
} | |
func (g *Game) Layout(w, h int) (int, int) { | |
return g.Width, g.Height | |
} | |
func main() { | |
ebiten.SetWindowTitle("Tetra3d - Lighting Test") | |
ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled) | |
game := NewGame() | |
if err := ebiten.RunGame(game); err != nil { | |
panic(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment