135 lines
2.7 KiB
Go
135 lines
2.7 KiB
Go
package sceneview
|
|
|
|
import (
|
|
"math"
|
|
|
|
"aego/core/mathx"
|
|
"aego/render/view"
|
|
)
|
|
|
|
type Mode uint8
|
|
|
|
const (
|
|
Mode2D Mode = iota
|
|
Mode3D
|
|
)
|
|
|
|
func (m Mode) String() string {
|
|
if m == Mode3D {
|
|
return "3D"
|
|
}
|
|
return "2D"
|
|
}
|
|
|
|
const (
|
|
orbitSpeed float32 = 0.006
|
|
minDistance float32 = 0.05
|
|
maxDistance float32 = 1e5
|
|
maxPitch float32 = 1.55
|
|
dollyBase = 0.85
|
|
)
|
|
|
|
type Camera3D struct {
|
|
Target mathx.Vec3
|
|
Distance float32
|
|
Yaw float32
|
|
Pitch float32
|
|
FOV float32
|
|
Ortho bool
|
|
}
|
|
|
|
func DefaultCamera3D() Camera3D {
|
|
return Camera3D{Distance: 10, Yaw: 0.6, Pitch: -0.45, FOV: 60}
|
|
}
|
|
|
|
func (c Camera3D) dist() float32 {
|
|
if c.Distance < minDistance {
|
|
return minDistance
|
|
}
|
|
return c.Distance
|
|
}
|
|
|
|
func (c Camera3D) fov() float32 {
|
|
f := c.FOV
|
|
if f <= 1 || f >= 179 {
|
|
f = 60
|
|
}
|
|
return mathx.Deg2Rad(f)
|
|
}
|
|
|
|
func (c Camera3D) Rotation() mathx.Quat {
|
|
return mathx.QuatYawPitch(c.Yaw, c.Pitch)
|
|
}
|
|
|
|
func (c Camera3D) Position() mathx.Vec3 {
|
|
return c.Target.Add(c.Rotation().Rotate(mathx.Vec3{Z: c.dist()}))
|
|
}
|
|
|
|
func (c Camera3D) Forward() mathx.Vec3 {
|
|
return c.Rotation().Rotate(mathx.Vec3{Z: -1})
|
|
}
|
|
|
|
func (c Camera3D) Right() mathx.Vec3 {
|
|
return c.Rotation().Rotate(mathx.Vec3{X: 1})
|
|
}
|
|
|
|
func (c Camera3D) Up() mathx.Vec3 {
|
|
return c.Rotation().Rotate(mathx.Vec3{Y: 1})
|
|
}
|
|
|
|
func (c Camera3D) Eye() mathx.Mat4 {
|
|
return mathx.Mat4TRS(c.Position(), c.Rotation(), mathx.Vec3{X: 1, Y: 1, Z: 1})
|
|
}
|
|
|
|
func (c Camera3D) clip() (float32, float32) {
|
|
d := c.dist()
|
|
return mathx.Clamp(d*0.002, 0.01, 1), max(d*50, 1000)
|
|
}
|
|
|
|
func (c Camera3D) halfHeight() float32 {
|
|
return c.dist() * float32(math.Tan(float64(c.fov())/2))
|
|
}
|
|
|
|
func (c Camera3D) View(vp mathx.Rect) view.View {
|
|
near, far := c.clip()
|
|
if c.Ortho {
|
|
return view.OrthoView(c.Eye(), c.halfHeight(), -far, far, vp)
|
|
}
|
|
return view.PerspectiveView(c.Eye(), c.fov(), near, far, vp)
|
|
}
|
|
|
|
func (c Camera3D) WorldPerPixel(h float32) float32 {
|
|
if h <= 0 {
|
|
h = 1
|
|
}
|
|
return 2 * c.halfHeight() / h
|
|
}
|
|
|
|
func (c *Camera3D) Orbit(dx, dy float32) {
|
|
c.Yaw -= dx * orbitSpeed
|
|
c.Pitch = mathx.Clamp(c.Pitch-dy*orbitSpeed, -maxPitch, maxPitch)
|
|
}
|
|
|
|
func (c *Camera3D) Pan(dx, dy, viewportH float32) {
|
|
w := c.WorldPerPixel(viewportH)
|
|
c.Target = c.Target.Sub(c.Right().Scale(dx * w)).Add(c.Up().Scale(dy * w))
|
|
}
|
|
|
|
func (c *Camera3D) Dolly(steps float32) {
|
|
c.Distance = mathx.Clamp(c.dist()*float32(math.Pow(dollyBase, float64(steps))), minDistance, maxDistance)
|
|
}
|
|
|
|
func (c *Camera3D) Frame(b mathx.AABB3) {
|
|
c.Target = b.Min.Add(b.Max).Scale(0.5)
|
|
r := b.Max.Sub(b.Min).Len() * 0.5
|
|
if r < 0.25 {
|
|
r = 0.25
|
|
}
|
|
half := float64(c.fov()) / 2
|
|
d := r / float32(math.Sin(half)) * 1.1
|
|
if c.Ortho {
|
|
d = r / float32(math.Tan(half)) * 1.1
|
|
}
|
|
c.Distance = mathx.Clamp(d, minDistance, maxDistance)
|
|
}
|