diff --git a/Attribution.md b/Attribution.md index d903815..bdcabe2 100644 --- a/Attribution.md +++ b/Attribution.md @@ -21,6 +21,11 @@ to my fragment shader. I had learned the Phong shading model at university, but this showed me how to actually apply it with OpenGL shaders. +[The OpenGL SuperBible][7] is a book that I've had for over a +decade. After finally wrapping my head around shaders from +LearnOpenGL.com, I went back to this book and reworked a lot of the +lighting models to get them into a much more understandable state. + [1]: https://open.gl/introduction [2]: https://learnopengl.com/Lighting/Basic-Lighting @@ -28,3 +33,4 @@ shaders. [4]: https://github.com/veandco/go-sdl2 [5]: https://github.com/veandco/go-sdl2-examples [6]: https://github.com/go-gl/example +[7]: https://www.opengl.org/sdk/docs/books/SuperBible/ diff --git a/Makefile b/Makefile index eff905d..904a8bf 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all: build +all: release dependencies: go mod tidy @@ -10,4 +10,15 @@ release: dependencies go build -x -v -ldflags "-s -w" cross_windows: - ./build_cross_windows.sh + export CGO_ENABLED=1 + export CC=x86_64-w64-mingw32-gcc + export GOOS=windows + export GOARCH=amd64 + go build -x -v -ldflags "-s -w" + +cross_windows_x86: + export CGO_ENABLED=1 + export CC=i686-w64-mingw32-gcc + export GOOS=windows + export GOARCH=386 + go build -x -v -ldflags "-s -w" diff --git a/Readme.md b/Readme.md index 81a7bfd..26a74d8 100644 --- a/Readme.md +++ b/Readme.md @@ -8,13 +8,13 @@ and OpenGL stuff. It ended up being a bit nicer than I expected. ## License/Copyright -This project is licensed under the [ISC License][1], a permissive open -source license. Essentially you can use this code for any purpose so -long as you acknowledge me for creating it. You can find the full text -in `License.md`. +This project is licensed under the [2-Clause BSD License][1], a +permissive open source license. Essentially you can use this code for +any purpose so long as you acknowledge me for creating it. You can +find the full text in `License.md`. Another file called `Attribution.md` contains links to the other projects used to build this one. -[1]: https://opensource.org/licenses/ISC +[1]: https://opensource.org/licenses/BSD-2-Clause diff --git a/build_cross_windows.sh b/build_cross_windows.sh index 5964668..feb07bb 100755 --- a/build_cross_windows.sh +++ b/build_cross_windows.sh @@ -1,11 +1,11 @@ #!/usr/bin/env sh export CGO_ENABLED=1 -export CC=i686-w64-mingw32-gcc -#export CC=x86_64-w64-mingw32-gcc -#export CXX=x86_64-w64-mingw32-g++ +#export CC=i686-w64-mingw32-gcc +export CC=x86_64-w64-mingw32-gcc export GOOS=windows -export GOARCH=386 +#export GOARCH=386 +export GOARCH=amd64 #export CGO_LDFLAGS="-lmingw32 -lSDL2 -lSDL2main" #export CGO_CFLAGS="-I/usr/x86_64-w64-mingw32/include -D_REENTRANT" diff --git a/go.mod b/go.mod index 55f5931..4740c8e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module gitea.wisellama.rocks/Wisellama/carpy-breakout go 1.17 require ( - github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f + github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784 github.com/go-gl/mathgl v1.0.0 github.com/veandco/go-sdl2 v0.4.10 ) diff --git a/go.sum b/go.sum index 7de1891..98f400c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f h1:s0O46d8fPwk9kU4k1jj76wBquMVETx7uveQD9MCIQoU= -github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM= +github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784 h1:1Zi56D0LNfvkzM+BdoxKryvUEdyWO7LP8oRT+oSYJW0= +github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= github.com/go-gl/mathgl v1.0.0 h1:t9DznWJlXxxjeeKLIdovCOVJQk/GzDEL7h/h+Ro2B68= github.com/go-gl/mathgl v1.0.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= github.com/veandco/go-sdl2 v0.4.10 h1:8QoD2bhWl7SbQDflIAUYWfl9Vq+mT8/boJFAUzAScgY= diff --git a/main.go b/main.go index f805c9f..47259d5 100644 --- a/main.go +++ b/main.go @@ -17,8 +17,6 @@ import ( ) var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") -var openglMode = flag.Bool("opengl", true, "Draw game using OpenGL rendering") -var sdlMode = flag.Bool("sdl", false, "Draw game using SDL rendering") const GameTitle = "Carpy Breakout" const LogFile = "output.log" @@ -44,11 +42,7 @@ func main() { // goroutine. runtime.LockOSThread() - if *sdlMode { - runSDL() - } else if *openglMode { - runOpenGL() - } + runOpenGL() } func runOpenGL() { @@ -59,18 +53,7 @@ func runOpenGL() { } defer sdl.Quit() - // Disable the Linux compositor flicker. - // https://github.com/mosra/magnum/issues/184#issuecomment-425952900 - sdl.SetHint(sdl.HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0") - - sdl.DisableScreenSaver() - - // Capture the mouse for movement - sdl.SetRelativeMouseMode(true) - - // Init and configure global settings - gl.Init() - sdl.GLSetAttribute(sdl.GL_MULTISAMPLESAMPLES, 2) // Smooth + sdlSettings() gameWindow, err := game_window.NewGameWindow(GameTitle) if err != nil { @@ -78,6 +61,20 @@ func runOpenGL() { } defer gameWindow.Destroy() + // GL Init requires an active OpenGL context (e.g. the game window) + err = gl.Init() + if err != nil { + log.Fatalf("Failed to initialize OpenGL: %v", err) + } + glVersion := gl.GetString(gl.VERSION) + if glVersion == nil { + log.Printf("Error getting OpenGL version.\n") + } + log.Printf("OpenGL Version: %v\n", gl.GoStr(glVersion)) + + // Then we can setup the OpenGL specific stuff in the game window + gameWindow.GLInit() + sunLight := gl_objects.NewPointLight(mgl32.Vec3{-10, 10, 30}) sunLight.GLInit(gameWindow.GLProgram) @@ -91,19 +88,22 @@ func runOpenGL() { // log.Fatal(err) // } - // TODO bounding box - // Brick Targets targets := breakout.NewTargets(4, 5, mgl32.Vec3{0, 6, 0}) targets.GLInit(gameWindow.GLProgram) for _, brick := range targets.Bricks { gameWindow.AddObject(brick) } - positiveBoundary := mgl32.Vec3{8, 16, 0} - negativeBoundary := mgl32.Vec3{-8, -16, 0} + positiveBoundary := targets.TopRight + negativeBoundary := positiveBoundary.Mul(-1) gameWindow.PositiveBoundary = positiveBoundary gameWindow.NegativeBoundary = negativeBoundary + // Bounding Box + boundingBoxMaterial := gl_objects.NewMaterial() + boundingBoxMaterial.Color = mgl32.Vec4{0.3, 0.3, 0.3, 1} + boundingBox := breakout.NewBoundingBox(negativeBoundary, positiveBoundary, boundingBoxMaterial) + // Paddle paddleMaterial := gl_objects.NewMaterial() paddleMaterial.Color = mgl32.Vec4{0, 1, 0, 1} @@ -119,12 +119,7 @@ func runOpenGL() { ball.GLInit(gameWindow.GLProgram) gameWindow.AddObject(ball) - // Global options after setup is all done - gl.ClearColor(0.0, 0.0, 0.0, 1.0) - gl.Enable(gl.DEPTH_TEST) - gl.DepthFunc(gl.LESS) - gl.LineWidth(2.0) - + glSettings() for gameWindow.IsRunning() { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) @@ -148,7 +143,25 @@ func setupLogging() { log.Println("=== START ===") } -func runSDL() { - // I really need to think through how to separate the physical objects from how they are drawn. - log.Println("TODO runSDL()") +func glSettings() { + // Global options after setup is all done + gl.ClearColor(0.0, 0.0, 0.0, 1.0) + gl.Enable(gl.DEPTH_TEST) + gl.DepthFunc(gl.LESS) + gl.LineWidth(2.0) +} + +func sdlSettings() { + // Smooth + sdl.GLSetAttribute(sdl.GL_MULTISAMPLEBUFFERS, 1) + sdl.GLSetAttribute(sdl.GL_MULTISAMPLESAMPLES, 4) + + // Disable the Linux compositor flicker. + // https://github.com/mosra/magnum/issues/184#issuecomment-425952900 + sdl.SetHint(sdl.HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0") + + sdl.DisableScreenSaver() + + // Capture the mouse for movement + sdl.SetRelativeMouseMode(true) } diff --git a/pkg/breakout/bounding_box.go b/pkg/breakout/bounding_box.go new file mode 100644 index 0000000..a8dd49a --- /dev/null +++ b/pkg/breakout/bounding_box.go @@ -0,0 +1,80 @@ +package breakout + +import ( + "github.com/go-gl/mathgl/mgl32" + + "gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/gl_objects" +) + +type BoundingBox struct { + Boxes []*gl_objects.Box +} + +func NewBoundingBox(lowerLeft, topRight mgl32.Vec3, material gl_objects.Material) *BoundingBox { + + var brickWidth float32 = 4.0 + var brickHeight float32 = 1.0 + var brickDepth float32 = 4.0 + + var widthOffset float32 = 0.5 + var heightOffset float32 = 1 + + bw := brickWidth + widthOffset + bh := brickHeight + heightOffset + + lowerLeftPos := mgl32.Vec3{ + bw/2 - float32(numColumns)*(bw)/2, + bh/2 - float32(numRows)*(bh)/2, + 0, + } + lowerLeftPos = lowerLeftPos.Add(adjustPos) + + topRightPos := mgl32.Vec3{ + bw/2 + float32(numColumns)*(bw)/2, + bh/2 + float32(numRows)*(bh)/2, + 0, + } + topRightPos = topRightPos.Add(adjustPos) + + targets.TopRight = topRightPos + targets.LowerLeft = lowerLeftPos + + materials := make([]*gl_objects.Material, numColumns) + + for i := 0; i < numColumns; i++ { + red := 0 + float32(i)/float32(numColumns) + blue := 1 - float32(i)/float32(numColumns) + m := gl_objects.NewMaterial() + m.Color = mgl32.Vec4{red, 0, blue, 1} + materials[i] = m + } + + n := 0 + for i := 0; i < numRows; i++ { + for j := 0; j < numColumns; j++ { + position := lowerLeftPos.Add(mgl32.Vec3{ + float32(j) * bw, + float32(i) * bh, + 0}) + targets.Bricks[n] = NewBrick(brickWidth, brickHeight, brickDepth, position, materials[i]) + n++ + } + } + + boundingBox := BoundingBox{ + Boxes: boxes, + } + return &boundingBox +} + +func (t *BoundingBox) GLInit(glProgram uint32) { + for _, brick := range t.Bricks { + brick.GLInit(glProgram) + } +} + +func (t *BoundingBox) GLDraw() { + for _, brick := range t.Bricks { + brick.GLDraw() + } +} diff --git a/pkg/breakout/targets.go b/pkg/breakout/targets.go index 007a23b..fa16b0e 100644 --- a/pkg/breakout/targets.go +++ b/pkg/breakout/targets.go @@ -7,9 +7,11 @@ import ( ) type Targets struct { - Rows int - Columns int - Bricks []*Brick + Rows int + Columns int + Bricks []*Brick + TopRight mgl32.Vec3 + LowerLeft mgl32.Vec3 } func NewTargets(numRows, numColumns int, adjustPos mgl32.Vec3) *Targets { @@ -36,6 +38,16 @@ func NewTargets(numRows, numColumns int, adjustPos mgl32.Vec3) *Targets { } lowerLeftPos = lowerLeftPos.Add(adjustPos) + topRightPos := mgl32.Vec3{ + bw/2 + float32(numColumns)*(bw)/2, + bh/2 + float32(numRows)*(bh)/2, + 0, + } + topRightPos = topRightPos.Add(adjustPos) + + targets.TopRight = topRightPos + targets.LowerLeft = lowerLeftPos + materials := make([]*gl_objects.Material, numColumns) for i := 0; i < numColumns; i++ { diff --git a/pkg/game_window/game_window.go b/pkg/game_window/game_window.go index a3dc68a..b35af6c 100644 --- a/pkg/game_window/game_window.go +++ b/pkg/game_window/game_window.go @@ -52,6 +52,7 @@ func NewGameWindow(title string) (*GameWindow, error) { log.Println("Failed creating SDL window") return nil, err } + gameWindow.SDLWindow = window context, err := window.GLCreateContext() if err != nil { @@ -60,18 +61,20 @@ func NewGameWindow(title string) (*GameWindow, error) { } gameWindow.GLContext = &context + return &gameWindow, err +} + +func (w *GameWindow) GLInit() error { program, err := gl_helpers.NewDefaultProgram() if err != nil { - panic(err) + return err } gl.UseProgram(program) - gameWindow.SDLWindow = window - gameWindow.GLProgram = program + w.GLProgram = program - gameWindow.WindowProjection() - - return &gameWindow, err + w.WindowProjection() + return nil } func (w *GameWindow) AddObject(object gl_objects.GLObject) { diff --git a/pkg/gl_helpers/gl_helpers.go b/pkg/gl_helpers/gl_helpers.go index ba1854e..edfe6d6 100644 --- a/pkg/gl_helpers/gl_helpers.go +++ b/pkg/gl_helpers/gl_helpers.go @@ -23,16 +23,22 @@ func NewProgram(vertexShaderSource string, fragmentShaderSource string) (uint32, if err != nil { return 0, err } + defer gl.DeleteShader(vertexShader) fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER) if err != nil { return 0, err } + defer gl.DeleteShader(fragmentShader) program := gl.CreateProgram() gl.AttachShader(program, vertexShader) + defer gl.DetachShader(program, vertexShader) + gl.AttachShader(program, fragmentShader) + defer gl.DetachShader(program, fragmentShader) + gl.LinkProgram(program) var status int32 @@ -47,19 +53,20 @@ func NewProgram(vertexShaderSource string, fragmentShaderSource string) (uint32, return 0, fmt.Errorf("failed to link program: %v", log) } - gl.DeleteShader(vertexShader) - gl.DeleteShader(fragmentShader) - return program, nil } func compileShader(source string, shaderType uint32) (uint32, error) { shader := gl.CreateShader(shaderType) + if shader == 0 { + return 0, fmt.Errorf("Error creating shader - type: %v", shaderType) + } sourceWithZero := source + "\x00" csources, free := gl.Strs(sourceWithZero) + defer free() + gl.ShaderSource(shader, 1, csources, nil) - free() gl.CompileShader(shader) var status int32 diff --git a/pkg/gl_objects/face.go b/pkg/gl_objects/face.go index ef850f5..602da9b 100644 --- a/pkg/gl_objects/face.go +++ b/pkg/gl_objects/face.go @@ -30,7 +30,6 @@ func NewFace( center, normal, mgl32.Vec2{0, 1}) - v1_2 := *v1 // bottom left v2 := newFaceVertex( @@ -47,7 +46,6 @@ func NewFace( center, normal, mgl32.Vec2{1, 0}) - v3_2 := *v3 // top right v4 := newFaceVertex( @@ -58,7 +56,7 @@ func NewFace( mgl32.Vec2{1, 1}) t1 := NewTriangle(v1, v2, v3) - t2 := NewTriangle(&v3_2, v4, &v1_2) + t2 := NewTriangle(v3, v4, v1) face := Face{ Material: material, diff --git a/pkg/gl_objects/point_light.go b/pkg/gl_objects/point_light.go index 8de58ee..bb62e51 100644 --- a/pkg/gl_objects/point_light.go +++ b/pkg/gl_objects/point_light.go @@ -17,7 +17,7 @@ type PointLight struct { func NewPointLight(position mgl32.Vec3) *PointLight { d := PointLight{ Position: position, - AmbientColor: mgl32.Vec4{0.1, 0.1, 0.1, 1}, + AmbientColor: mgl32.Vec4{0.2, 0.2, 0.2, 1}, DiffuseColor: mgl32.Vec4{1, 1, 1, 1}, SpecularColor: mgl32.Vec4{1, 1, 1, 1}, }