commit 1ec5b8b3dd5c6bd525846632517b91f1b97d2c84
parent 73e44436dd889b8a44ef3d90175e20983653b7c2
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Fri,  8 Nov 2024 11:55:46 +0900
move camera
Diffstat:
3 files changed, 166 insertions(+), 13 deletions(-)
diff --git a/cmd/sample/main.go b/cmd/sample/main.go
@@ -17,6 +17,8 @@ const (
 	winW, winH = 800, 600
 )
 
+var camera *tofu.Camera
+
 func init() {
 	runtime.LockOSThread()
 }
@@ -28,7 +30,8 @@ func framebufferSizeCallback(w *glfw.Window, width int, height int) {
 	gl.Viewport(0, 0, int32(width), int32(height))
 }
 
-func processInput(w *glfw.Window) {
+func keyboardCallback(w *glfw.Window) {
+	const speed = 0.3
 	if w.GetKey(glfw.KeyQ) == glfw.Press {
 		w.SetShouldClose(true)
 	}
@@ -44,33 +47,106 @@ func processInput(w *glfw.Window) {
 			alpha = 0
 		}
 	}
+	if w.GetKey(glfw.KeyW) == glfw.Press {
+		camera.Move(0, 0, -speed)
+	}
+	if w.GetKey(glfw.KeyS) == glfw.Press {
+		camera.Move(0, 0, speed)
+	}
+	if w.GetKey(glfw.KeyA) == glfw.Press {
+		camera.Move(-speed, 0, 0)
+	}
+	if w.GetKey(glfw.KeyD) == glfw.Press {
+		camera.Move(speed, 0, 0)
+	}
+	if w.GetKey(glfw.KeySpace) == glfw.Press {
+		camera.Move(0, speed, 0)
+	}
+	if w.GetKey(glfw.KeyLeftShift) == glfw.Press {
+		camera.Move(0, -speed, 0)
+	}
+}
+
+var (
+	cursorX, cursorY float32
+	mouseCallbackFirstTime = true
+)
+
+func mouseCallback(w *glfw.Window, x, y float64) {
+	const sensitivity = 0.01
+	if mouseCallbackFirstTime {
+		cursorX, cursorY = float32(x), float32(y)
+		mouseCallbackFirstTime = false
+		return
+	}
+	dx, dy := cursorX - float32(x), float32(y) - cursorY
+	cursorX, cursorY = float32(x), float32(y)
+	dx *= sensitivity
+	dy *= sensitivity
+	camera.Yaw(dx)
+	camera.Pitch(dy)
 }
 
 var object = tofu.Object{
 	Vertices: []tofu.Point3D{
-		tofu.Point3D{0.5, 0.5, 0.0},
-		tofu.Point3D{0.5, -0.5, 0.0},
-		tofu.Point3D{-0.5, -0.5, 0.0},
-		tofu.Point3D{-0.5, 0.5, 0.0},
+		tofu.Point3D{0.5, 0.5, -0.5},
+		tofu.Point3D{0.5, -0.5, -0.5},
+		tofu.Point3D{-0.5, -0.5, -0.5},
+		tofu.Point3D{-0.5, 0.5, -0.5},
+		tofu.Point3D{0.5, 0.5, 0.5},
+		tofu.Point3D{0.5, -0.5, 0.5},
+		tofu.Point3D{-0.5, -0.5, 0.5},
+		tofu.Point3D{-0.5, 0.5, 0.5},
 	},
 	Colors: []color.Color{
 		color.RGBA{255, 0, 0, 255},
 		color.RGBA{0, 255, 0, 255},
 		color.RGBA{0, 0, 255, 255},
 		color.RGBA{255, 255, 0, 255},
+		color.RGBA{255, 0, 0, 255},
+		color.RGBA{0, 255, 0, 255},
+		color.RGBA{0, 0, 255, 255},
+		color.RGBA{255, 255, 0, 255},
 	},
 	TexCoords: []tofu.Point2D{
 		tofu.Point2D{1.0, 1.0},
 		tofu.Point2D{1.0, 0.0},
 		tofu.Point2D{0.0, 0.0},
 		tofu.Point2D{0.0, 1.0},
+		tofu.Point2D{1.0, 1.0},
+		tofu.Point2D{1.0, 0.0},
+		tofu.Point2D{0.0, 0.0},
+		tofu.Point2D{0.0, 1.0},
 	},
 	Faces: [][3]uint32{
 		{0, 1, 2},
 		{0, 2, 3},
+		{0, 1, 5},
+		{0, 5, 4},
+		{1, 2, 6},
+		{1, 6, 5},
+		{2, 3, 7},
+		{2, 7, 6},
+		{3, 0, 4},
+		{3, 4, 7},
+		{4, 5, 6},
+		{4, 6, 7},
 	},
 }
 
+var cubePositions = []tofu.Vec3{
+	{0, 0, 0},
+	{2, 5, -15},
+	{-1.5, -2.2, -2.5},
+	{-3.8, -2, -12.3},
+	{2.4, -0.4, -3.5},
+	{-1.7, 3.0, -7.5},
+	{1.3, -2.0, -2.5},
+	{1.5, 2.0, -2.5},
+	{1.5, 0.2, -1.5},
+	{-1.3, 1.0, -1.5},
+}
+
 var alpha float32 = 0.2
 
 func main() {
@@ -122,18 +198,32 @@ func main() {
 	if err := program.SetTexture(texture2, "texture2"); err != nil {
 		log.Fatalf("set texture: %v", err)
 	}
+	gl.Enable(gl.DEPTH_TEST)
 
-	window.SetFramebufferSizeCallback(framebufferSizeCallback)
+	camera = tofu.NewCamera()
+	camera.MoveTo(tofu.Vec3{0, 0, 3})
+	camera.LookAt(tofu.Vec3{0, 0, 0})
 
-	transform := tofu.Rotate(math.Pi/4, tofu.Vec3{0, 0, 1})
+	window.SetFramebufferSizeCallback(framebufferSizeCallback)
+	window.SetCursorPosCallback(mouseCallback)
+	window.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
 
 	for !window.ShouldClose() {
-		processInput(window)
+		keyboardCallback(window)
 		gl.ClearColor(0.2, 0.3, 0.3, 1.0)
-		gl.Clear(gl.COLOR_BUFFER_BIT)
+		gl.Clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT)
 		program.SetFloat32("alpha", alpha)
-		program.SetMat4("transform", transform)
-		gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil)
+		view := camera.View()
+		projection := tofu.Perspective(80*math.Pi/180, 800/600, 0.1, 100)
+		program.SetMat4("view", view)
+		program.SetMat4("projection", projection)
+		for _, p := range cubePositions {
+			model := tofu.Translate(p).
+				Mul(tofu.Rotate(float32(glfw.GetTime()), tofu.Vec3{0, 0, 1})).
+				Mul(tofu.Rotate(float32(glfw.GetTime()), tofu.Vec3{0, 1, 0}))
+			program.SetMat4("model", model)
+			gl.DrawElements(gl.TRIANGLES, 36, gl.UNSIGNED_INT, nil)
+		}
 		window.SwapBuffers()
 		glfw.PollEvents()
 	}
diff --git a/cmd/sample/vertex.glsl b/cmd/sample/vertex.glsl
@@ -4,9 +4,9 @@ layout (location = 1) in vec3 col;
 layout (location = 2) in vec2 vtexCoord;
 out vec3 vcol;
 out vec2 texCoord;
-uniform mat4 transform;
+uniform mat4 projection, view, model;
 void main() {
-	gl_Position = transform * vec4(pos, 1.0);
+	gl_Position = projection * view * model * vec4(pos, 1.0);
 	vcol = col;
 	texCoord = vtexCoord;
 }
diff --git a/math.go b/math.go
@@ -4,6 +4,55 @@ import "math"
 
 type Vec3 [3]float32
 
+
+func (v Vec3) Normalize() Vec3 {
+	r := float32(math.Sqrt(float64(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])))
+	return Vec3{v[0] / r, v[1] / r, v[2] / r}
+}
+
+func (v Vec3) Add(w Vec3) Vec3 {
+	return Vec3{v[0]+w[0], v[1]+w[1], v[2]+w[2]}
+}
+
+func (v Vec3) Sub(w Vec3) Vec3 {
+	return Vec3{v[0]-w[0], v[1]-w[1], v[2]-w[2]}
+}
+
+func (v Vec3) Inverse() Vec3 {
+	return Vec3{-v[0], -v[1], -v[2]}
+}
+
+func (v Vec3) Cross(w Vec3) Vec3 {
+	return Vec3{
+		v[1]*w[2] - v[2]*w[1],
+		v[2]*w[0] - v[0]*w[2],
+		v[0]*w[1] - v[1]*w[0],
+	}
+}
+
+func (v Vec3) MulF(x float32) Vec3{
+	return Vec3{v[0]*x, v[1]*x, v[2]*x}
+}
+
+type Mat3 [9]float32
+
+func Rotate3(rad float32, axis Vec3) Mat3 {
+	x, y, z := axis[0], axis[1], axis[2]
+	c := float32(math.Cos(float64(rad)))
+	d := 1 - c
+	s := float32(math.Sin(float64(rad)))
+	return Mat3{c + x*x*d, y*x*d + z*s, z*x*d - y*s,
+		x*y*d - z*s, c + y*y*d, z*y*d + x*s,
+		x*z*d + y*s, y*z*d - x*s, c + z*z*d}
+}
+
+func (m Mat3) MulV(v Vec3) Vec3 {
+	return Vec3{
+		m[0]*v[0]+m[3]*v[1]+m[6]*v[2],
+		m[1]*v[0]+m[4]*v[1]+m[7]*v[2],
+		m[2]*v[0]+m[5]*v[1]+m[8]*v[2]}
+}
+
 // column major
 type Mat4 [16]float32
 
@@ -42,3 +91,17 @@ func Rotate(rad float32, axis Vec3) Mat4 {
 		x*z*d + y*s, y*z*d - x*s, c + z*z*d, 0,
 		0, 0, 0, 1}
 }
+
+func Perspective(fov, asp, min, max float32) Mat4 {
+	tan := float32(math.Tan(float64(fov/2)))
+	w := min * tan
+	h := w / asp
+
+	var mat Mat4
+	mat[0] = min / w
+	mat[5] = min / h
+	mat[10] = -(max + min) / (max - min)
+	mat[11] = -1
+	mat[14] = -(2 * min * max) / (max - min)
+	return mat
+}