tofu

Making something with OpenGL in Go
Log | Files | Refs

main.go (10621B)


      1 package main
      2 
      3 import (
      4 	"log"
      5 	"math"
      6 	"math/rand/v2"
      7 	"path/filepath"
      8 	"runtime"
      9 	"time"
     10 
     11 	"git.mtkn.jp/tofu"
     12 )
     13 
     14 const (
     15 	winW, winH = 800, 600
     16 )
     17 
     18 var camera *tofu.Camera
     19 
     20 func init() {
     21 	runtime.LockOSThread()
     22 }
     23 
     24 func processInput(app *App) {
     25 	const speed = 0.05
     26 	if tofu.IsKeyPressed(tofu.KeyQ) {
     27 		app.termination = true
     28 		return
     29 	}
     30 	if tofu.IsKeyPressed(tofu.KeyW) {
     31 		camera.Move2(0, -speed)
     32 	}
     33 	if tofu.IsKeyPressed(tofu.KeyS) {
     34 		camera.Move2(0, speed)
     35 	}
     36 	if tofu.IsKeyPressed(tofu.KeyA) {
     37 		camera.Move2(-speed, 0)
     38 	}
     39 	if tofu.IsKeyPressed(tofu.KeyD) {
     40 		camera.Move2(speed, 0)
     41 	}
     42 	if tofu.IsKeyPressed(tofu.KeySpace) {
     43 		camera.MoveVert(speed)
     44 	}
     45 	if tofu.IsKeyPressed(tofu.KeyLeftShift) {
     46 		camera.MoveVert(-speed)
     47 	}
     48 }
     49 
     50 func watchCursor(app *App) {
     51 	const sensitivity = 0.005
     52 	firstTime := true
     53 	for cur := range app.cursorChan {
     54 		if firstTime {
     55 			app.cur = cur
     56 			firstTime = false
     57 			continue
     58 		}
     59 		dx, dy := float32(app.cur.X-cur.X), float32(app.cur.Y-cur.Y)
     60 		dx *= sensitivity
     61 		dy *= sensitivity
     62 		app.cur = cur
     63 		camera.YawPitch(dx, -dy)
     64 	}
     65 }
     66 
     67 // directional light
     68 type DirLight struct {
     69 	Dir      tofu.Vec3
     70 	Ambient  tofu.Vec3
     71 	Diffuse  tofu.Vec3
     72 	Specular tofu.Vec3
     73 }
     74 
     75 type SpotLight struct {
     76 	Pos         tofu.Vec3
     77 	Dir         tofu.Vec3
     78 	Ambient     tofu.Vec3
     79 	Diffuse     tofu.Vec3
     80 	Specular    tofu.Vec3
     81 	Kc, Kl, Kq  float32
     82 	CutOff      float32
     83 	OuterCutOff float32
     84 }
     85 
     86 type PointLight struct {
     87 	Pos        tofu.Vec3
     88 	Ambient    tofu.Vec3
     89 	Diffuse    tofu.Vec3
     90 	Specular   tofu.Vec3
     91 	Kc, Kl, Kq float32
     92 }
     93 
     94 var (
     95 	sunCol = tofu.Vec3{1, 1, 1}
     96 	sun    = &DirLight{
     97 		Dir:      tofu.Vec3{-0.2, -1.0, -0.3},
     98 		Ambient:  sunCol.MulF(0.5),
     99 		Diffuse:  sunCol.MulF(0.3),
    100 		Specular: sunCol,
    101 	}
    102 	spotLightCol = tofu.Vec3{1, 1, 1}
    103 	spotLight    = &SpotLight{
    104 		Ambient:     spotLightCol.MulF(0.5),
    105 		Diffuse:     spotLightCol.MulF(0.3),
    106 		Specular:    spotLightCol,
    107 		CutOff:      float32(math.Cos(25 * math.Pi / 180)),
    108 		OuterCutOff: float32(math.Cos(35 * math.Pi / 180)),
    109 		Kc:          1.0,
    110 		Kl:          0.0009,
    111 		Kq:          0.00032,
    112 	}
    113 	pointLights           = make([]PointLight, 10)
    114 	pointLightDiv float32 = 10.0
    115 )
    116 
    117 var teapot *tofu.Object
    118 var cube = &tofu.Object{
    119 	Vertices: []tofu.Vec3{
    120 		tofu.Vec3{0.5, 0.5, -0.5},
    121 		tofu.Vec3{0.5, -0.5, -0.5},
    122 		tofu.Vec3{-0.5, -0.5, -0.5},
    123 		tofu.Vec3{-0.5, 0.5, -0.5},
    124 		tofu.Vec3{0.5, 0.5, 0.5},
    125 		tofu.Vec3{0.5, -0.5, 0.5},
    126 		tofu.Vec3{-0.5, -0.5, 0.5},
    127 		tofu.Vec3{-0.5, 0.5, 0.5},
    128 	},
    129 	Normals: []tofu.Vec3{
    130 		{0.0, 0.0, -1.0},
    131 		{1.0, 0.0, 0.0},
    132 		{0.0, -1.0, 0.0},
    133 		{-1.0, 0.0, 0.0},
    134 		{0.0, 1.0, 0.0},
    135 		{0.0, 0.0, 1.0},
    136 	},
    137 	TexCoords: []tofu.Vec2{
    138 		tofu.Vec2{1.0, 1.0},
    139 		tofu.Vec2{1.0, 0.0},
    140 		tofu.Vec2{0.0, 0.0},
    141 		tofu.Vec2{0.0, 1.0},
    142 	},
    143 	Faces: []tofu.Face{
    144 		{{0, 0, 0}, {1, 0, 1}, {2, 0, 2}},
    145 		{{0, 0, 0}, {2, 0, 2}, {3, 0, 3}},
    146 		{{0, 1, 0}, {1, 1, 1}, {5, 1, 2}},
    147 		{{0, 1, 0}, {5, 1, 2}, {4, 1, 3}},
    148 		{{1, 2, 0}, {2, 2, 1}, {6, 2, 2}},
    149 		{{1, 2, 0}, {6, 2, 2}, {5, 2, 3}},
    150 		{{2, 3, 0}, {3, 3, 1}, {7, 3, 2}},
    151 		{{2, 3, 0}, {7, 3, 2}, {6, 3, 3}},
    152 		{{3, 4, 0}, {0, 4, 1}, {4, 4, 2}},
    153 		{{3, 4, 0}, {4, 4, 2}, {7, 4, 3}},
    154 		{{4, 5, 0}, {5, 5, 1}, {6, 5, 2}},
    155 		{{4, 5, 0}, {6, 5, 2}, {7, 5, 3}},
    156 	},
    157 }
    158 var light = &tofu.Object{
    159 	Vertices: []tofu.Vec3{
    160 		tofu.Vec3{0.5, 0.5, -0.5},
    161 		tofu.Vec3{0.5, -0.5, -0.5},
    162 		tofu.Vec3{-0.5, -0.5, -0.5},
    163 		tofu.Vec3{-0.5, 0.5, -0.5},
    164 		tofu.Vec3{0.5, 0.5, 0.5},
    165 		tofu.Vec3{0.5, -0.5, 0.5},
    166 		tofu.Vec3{-0.5, -0.5, 0.5},
    167 		tofu.Vec3{-0.5, 0.5, 0.5},
    168 	},
    169 	Normals: []tofu.Vec3{
    170 		{0.0, 0.0, -1.0},
    171 		{1.0, 0.0, 0.0},
    172 		{0.0, -1.0, 0.0},
    173 		{-1.0, 0.0, 0.0},
    174 		{0.0, 1.0, 0.0},
    175 		{0.0, 0.0, 1.0},
    176 	},
    177 	Faces: []tofu.Face{
    178 		{{0, 0, tofu.NoIndex}, {1, 0, tofu.NoIndex}, {2, 0, tofu.NoIndex}},
    179 		{{0, 0, tofu.NoIndex}, {2, 0, tofu.NoIndex}, {3, 0, tofu.NoIndex}},
    180 		{{0, 1, tofu.NoIndex}, {1, 1, tofu.NoIndex}, {5, 1, tofu.NoIndex}},
    181 		{{0, 1, tofu.NoIndex}, {5, 1, tofu.NoIndex}, {4, 1, tofu.NoIndex}},
    182 		{{1, 2, tofu.NoIndex}, {2, 2, tofu.NoIndex}, {6, 2, tofu.NoIndex}},
    183 		{{1, 2, tofu.NoIndex}, {6, 2, tofu.NoIndex}, {5, 2, tofu.NoIndex}},
    184 		{{2, 3, tofu.NoIndex}, {3, 3, tofu.NoIndex}, {7, 3, tofu.NoIndex}},
    185 		{{2, 3, tofu.NoIndex}, {7, 3, tofu.NoIndex}, {6, 3, tofu.NoIndex}},
    186 		{{3, 4, tofu.NoIndex}, {0, 4, tofu.NoIndex}, {4, 4, tofu.NoIndex}},
    187 		{{3, 4, tofu.NoIndex}, {4, 4, tofu.NoIndex}, {7, 4, tofu.NoIndex}},
    188 		{{4, 5, tofu.NoIndex}, {5, 5, tofu.NoIndex}, {6, 5, tofu.NoIndex}},
    189 		{{4, 5, tofu.NoIndex}, {6, 5, tofu.NoIndex}, {7, 5, tofu.NoIndex}},
    190 	},
    191 }
    192 
    193 type Trans struct {
    194 	Projection, View, Model tofu.Mat4
    195 }
    196 
    197 var cubeTrans = new(Trans)
    198 
    199 var cubePositions = []tofu.Vec3{
    200 	{4.24, 0.94, -1.94},
    201 	{0.79, 2.40, 2.87},
    202 	{-0.64, -1.68, 2.79},
    203 	{-3.99, 2.85, 3.35},
    204 	{2.61, -0.04, -0.74},
    205 	{4.46, 3.22, 2.09},
    206 	{-3.42, -3.80, 4.10},
    207 	{3.68, -0.51, 2.05},
    208 	{-1.00, 1.45, 1.96},
    209 	{-2.00, 0.92, 4.57},
    210 }
    211 
    212 type Material struct {
    213 	Diffuse  tofu.Vec3
    214 	Specular tofu.Vec3
    215 	Shiness  float32
    216 }
    217 
    218 type MaterialTexture struct {
    219 	Diffuse  *tofu.Texture
    220 	Specular *tofu.Texture
    221 	Shiness  float32
    222 }
    223 
    224 var cubeUniforms = struct {
    225 	CamPos tofu.Vec3
    226 	Trans  Trans
    227 	Cube   MaterialTexture
    228 	//	Sun         *DirLight
    229 	Light       *SpotLight
    230 	PointLights []PointLight
    231 }{
    232 	Cube: MaterialTexture{Shiness: 32},
    233 	//	Sun:   sun,
    234 	Light: spotLight,
    235 }
    236 
    237 var teapotUniforms = struct {
    238 	CamPos tofu.Vec3
    239 	Trans  Trans
    240 	//Sun         *DirLight
    241 	Light *SpotLight
    242 	// PointLights []PointLight
    243 }{
    244 	//Sun:   sun,
    245 	Light: spotLight,
    246 }
    247 
    248 var lightUniforms = struct {
    249 	Trans    Trans
    250 	LightCol tofu.Vec3
    251 }{
    252 	LightCol: spotLight.Specular,
    253 }
    254 
    255 type App struct {
    256 	program       *tofu.Program
    257 	lightProgram  *tofu.Program
    258 	teapotProgram *tofu.Program
    259 	cursorChan    chan tofu.Cursor
    260 	cur           tofu.Cursor
    261 	keyChan       chan struct{}
    262 	camera        *tofu.Camera
    263 	termination   bool
    264 	startedAt     time.Time
    265 	cubeRotate    []struct {
    266 		vel  float32
    267 		axis tofu.Vec3
    268 	}
    269 }
    270 
    271 func (app *App) Update() error {
    272 	now := float32(time.Since(app.startedAt).Seconds())
    273 	processInput(app)
    274 	if app.termination {
    275 		return tofu.Termination
    276 	}
    277 
    278 	view := camera.View()
    279 	projection := tofu.Perspective(80*math.Pi/180, 800/600, 0.1, 100)
    280 
    281 	lightModel := tofu.Rotate(now, tofu.Vec3{0, 1, 0}).
    282 		Mul(tofu.Translate(tofu.Vec3{5, 4.5, 0})).
    283 		Mul(tofu.Scale(0.3))
    284 	spotLight.Pos = tofu.Rotate3(now, tofu.Vec3{0, 1, 0}).
    285 		MulV(tofu.Vec3{5, 4.5, 0})
    286 	spotLight.Dir = tofu.Vec3{0, 0, 0}.Sub(spotLight.Pos)
    287 
    288 	app.program.Use()
    289 	cubeUniforms.Trans.View = view
    290 	cubeUniforms.Trans.Projection = projection
    291 	cubeUniforms.CamPos = camera.Pos
    292 	for i, p := range cubePositions {
    293 		model := tofu.Translate(p.Inverse()).
    294 			Mul(tofu.Rotate(now*app.cubeRotate[i].vel, app.cubeRotate[i].axis))
    295 		cubeUniforms.Trans.Model = model
    296 		if err := app.program.SetUniforms(); err != nil {
    297 			log.Println("program.setuniforms:", err)
    298 		}
    299 		cube.Draw(app.program)
    300 	}
    301 	app.teapotProgram.Use()
    302 	teapotUniforms.Trans.View = view
    303 	teapotUniforms.Trans.Projection = projection
    304 	teapotUniforms.Trans.Model = tofu.Translate(tofu.Vec3{0, 0, 0}).
    305 		Mul(tofu.Scale(0.3))
    306 	teapotUniforms.CamPos = camera.Pos
    307 	if err := app.teapotProgram.SetUniforms(); err != nil {
    308 		log.Printf("teapotprogram.setuniforms(): %v", err)
    309 	}
    310 	teapot.Draw(app.teapotProgram)
    311 
    312 	app.lightProgram.Use()
    313 	lightUniforms.Trans.View = view
    314 	lightUniforms.Trans.Projection = projection
    315 	lightUniforms.Trans.Model = lightModel
    316 	lightUniforms.LightCol = spotLightCol
    317 	if err := app.lightProgram.SetUniforms(); err != nil {
    318 		log.Printf("lightprogram.setuniforms(): %v", err)
    319 	}
    320 	light.Draw(app.lightProgram)
    321 	for _, pl := range pointLights {
    322 		lightUniforms.Trans.Model = tofu.Translate(pl.Pos).
    323 			Mul(tofu.Scale(0.3))
    324 		lightUniforms.LightCol = tofu.Vec3{1, 1, 1}.Sub(tofu.Vec3{1, 1, 1}.Sub(pl.Specular).MulF(0.3))
    325 		if err := app.lightProgram.SetUniforms(); err != nil {
    326 			log.Printf("lightprogram.setuniforms(): %v", err)
    327 		}
    328 		light.Draw(app.lightProgram)
    329 	}
    330 
    331 	return nil
    332 }
    333 
    334 func (app *App) CursorChan() chan<- tofu.Cursor {
    335 	return app.cursorChan
    336 }
    337 
    338 func main() {
    339 	var err error
    340 	app := &App{}
    341 	app.cursorChan = make(chan tofu.Cursor)
    342 	app.startedAt = time.Now()
    343 	app.cubeRotate = make([]struct {
    344 		vel  float32
    345 		axis tofu.Vec3
    346 	}, len(cubePositions))
    347 	for i := 0; i < len(app.cubeRotate); i++ {
    348 		app.cubeRotate[i] = struct {
    349 			vel  float32
    350 			axis tofu.Vec3
    351 		}{
    352 			vel:  rand.Float32(),
    353 			axis: tofu.Vec3{rand.Float32(), rand.Float32(), rand.Float32()}.Normalize(),
    354 		}
    355 	}
    356 	go watchCursor(app)
    357 	tofu.SetWindowSize(winW, winH)
    358 	tofu.SetWindowTitle("Sample")
    359 	tofu.Init()
    360 
    361 	cube.Load()
    362 	light.Load()
    363 
    364 	_, f, _, ok := runtime.Caller(0)
    365 	if !ok {
    366 		log.Fatalf("unable to get source file information")
    367 	}
    368 	vpath := filepath.Join(filepath.Dir(f), "vertex.glsl")
    369 	fpath := filepath.Join(filepath.Dir(f), "fragment.glsl")
    370 	lightFpath := filepath.Join(filepath.Dir(f), "light_fragment.glsl")
    371 	texturePath := filepath.Join(filepath.Dir(f), "container2.png")
    372 	specularMapPath := filepath.Join(filepath.Dir(f), "container2_specular.png")
    373 	teapotPath := filepath.Join(filepath.Dir(f), "backpack/backpack.obj")
    374 	teapotFpath := filepath.Join(filepath.Dir(f), "teapot.glsl")
    375 
    376 	teapot, err = tofu.DecodeObject(teapotPath)
    377 	if err != nil {
    378 		log.Fatal(err)
    379 	}
    380 	//teapot.SetNormals()
    381 	teapot.Load()
    382 
    383 	app.program, err = tofu.NewProgram(vpath, fpath)
    384 	if err != nil {
    385 		log.Fatalf("create shader program: %v", err)
    386 	}
    387 	app.program.Uniforms = &cubeUniforms
    388 	app.lightProgram, err = tofu.NewProgram(vpath, lightFpath)
    389 	if err != nil {
    390 		log.Fatalf("create light shader program: %v", err)
    391 	}
    392 	app.lightProgram.Uniforms = &lightUniforms
    393 
    394 	app.teapotProgram, err = tofu.NewProgram(vpath, teapotFpath)
    395 	if err != nil {
    396 		log.Fatalf("create teapot shader program: %v", err)
    397 	}
    398 	app.teapotProgram.Uniforms = &teapotUniforms
    399 
    400 	cubeUniforms.Cube.Diffuse, err = tofu.NewTexture(texturePath)
    401 	if err != nil {
    402 		log.Fatalf("NewTesture: %v", err)
    403 	}
    404 	cubeUniforms.Cube.Specular, err = tofu.NewTexture(specularMapPath)
    405 	if err != nil {
    406 		log.Fatalf("NewTesture: %v", err)
    407 	}
    408 	for i, _ := range pointLights {
    409 		col := tofu.Vec3{rand.Float32(), rand.Float32(), rand.Float32()}
    410 		pointLights[i] = PointLight{
    411 			Pos: tofu.Vec3{
    412 				(rand.Float32() - 0.5) * pointLightDiv,
    413 				(rand.Float32() - 0.5) * pointLightDiv,
    414 				(rand.Float32() - 0.5) * pointLightDiv},
    415 			Ambient:  col.MulF(0.3),
    416 			Diffuse:  col.MulF(0.6),
    417 			Specular: col,
    418 			Kc:       1.0,
    419 			Kl:       0.009,
    420 			Kq:       0.0032,
    421 		}
    422 	}
    423 	cubeUniforms.PointLights = pointLights
    424 	//	teapotUniforms.PointLights = pointLights
    425 	camera = tofu.NewCamera()
    426 	camera.MoveTo(tofu.Vec3{0, 0, 3})
    427 	camera.LookAt(tofu.Vec3{0, 0, 0})
    428 
    429 	if err := tofu.Run(app); err != nil {
    430 		log.Fatal(err)
    431 	}
    432 }