tofu

Making something with OpenGL in Go
Log | Files | Refs

main.go (8722B)


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