├── .gitignore ├── res ├── textures │ ├── MEDIA0.png │ ├── PISFA0.png │ ├── PISGB0.png │ ├── SSWVA1.png │ ├── SSWVB1.png │ ├── SSWVC1.png │ ├── SSWVD1.png │ ├── SSWVE0.png │ ├── SSWVF0.png │ ├── SSWVG0.png │ ├── SSWVH0.png │ ├── SSWVI0.png │ ├── SSWVJ0.png │ ├── SSWVK0.png │ ├── SSWVL0.png │ ├── SSWVM0.png │ └── WolfCollection.png └── shaders │ ├── basicFragment120.fs │ ├── basicFragment.fs │ ├── basicVertex120.vs │ ├── basicVertex.vs │ ├── phongVertex.vs │ └── phongFragment.fs ├── go.mod ├── Makefile ├── go.sum ├── src ├── material.go ├── object.go ├── util.go ├── quaternion.go ├── game.go ├── transform.go ├── camera.go ├── texture.go ├── medkit.go ├── mesh.go ├── gl │ ├── gl.go │ └── generate │ │ └── generate.go ├── vectors.go ├── shader.go ├── matrix.go ├── door.go ├── map.go ├── main.go ├── player.go ├── monster.go └── level.go ├── maps ├── level1.map ├── levelTest.map ├── level3.map └── level2.map ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | -------------------------------------------------------------------------------- /res/textures/MEDIA0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/MEDIA0.png -------------------------------------------------------------------------------- /res/textures/PISFA0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/PISFA0.png -------------------------------------------------------------------------------- /res/textures/PISGB0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/PISGB0.png -------------------------------------------------------------------------------- /res/textures/SSWVA1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVA1.png -------------------------------------------------------------------------------- /res/textures/SSWVB1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVB1.png -------------------------------------------------------------------------------- /res/textures/SSWVC1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVC1.png -------------------------------------------------------------------------------- /res/textures/SSWVD1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVD1.png -------------------------------------------------------------------------------- /res/textures/SSWVE0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVE0.png -------------------------------------------------------------------------------- /res/textures/SSWVF0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVF0.png -------------------------------------------------------------------------------- /res/textures/SSWVG0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVG0.png -------------------------------------------------------------------------------- /res/textures/SSWVH0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVH0.png -------------------------------------------------------------------------------- /res/textures/SSWVI0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVI0.png -------------------------------------------------------------------------------- /res/textures/SSWVJ0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVJ0.png -------------------------------------------------------------------------------- /res/textures/SSWVK0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVK0.png -------------------------------------------------------------------------------- /res/textures/SSWVL0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVL0.png -------------------------------------------------------------------------------- /res/textures/SSWVM0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/SSWVM0.png -------------------------------------------------------------------------------- /res/textures/WolfCollection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdm85/wolfengo/HEAD/res/textures/WolfCollection.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gdm85/wolfengo 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 7 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 8 | ) 9 | -------------------------------------------------------------------------------- /res/shaders/basicFragment120.fs: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | varying vec2 texCoord0; 4 | 5 | uniform vec3 color; 6 | uniform sampler2D sampler; 7 | 8 | void main() 9 | { 10 | gl_FragColor = texture2D(sampler, texCoord0.xy) * vec4(color, 1); 11 | } 12 | -------------------------------------------------------------------------------- /res/shaders/basicFragment.fs: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | in vec2 texCoord0; 4 | 5 | out vec4 fragColor; 6 | 7 | uniform vec3 color; 8 | uniform sampler2D sampler; 9 | 10 | void main() 11 | { 12 | fragColor = texture(sampler, texCoord0.xy) * vec4(color, 1); 13 | } -------------------------------------------------------------------------------- /res/shaders/basicVertex120.vs: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | attribute vec3 position; 4 | attribute vec2 texCoord; 5 | 6 | varying vec2 texCoord0; 7 | 8 | uniform mat4 transform; 9 | 10 | void main() 11 | { 12 | gl_Position = transform * vec4(position, 1.0); 13 | texCoord0 = texCoord; 14 | } -------------------------------------------------------------------------------- /res/shaders/basicVertex.vs: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout (location = 0) in vec3 position; 4 | layout (location = 1) in vec2 texCoord; 5 | 6 | out vec2 texCoord0; 7 | 8 | uniform mat4 transform; 9 | 10 | void main() 11 | { 12 | gl_Position = transform * vec4(position, 1.0); 13 | texCoord0 = texCoord; 14 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | ifeq ($(shell uname),Darwin) 3 | GL_VER:=v4.1-core 4 | else 5 | GL_VER:=v2.1 6 | endif 7 | 8 | all: wolfengo test 9 | 10 | wolfengo: gl 11 | go build -o bin/wolfengo ./src 12 | 13 | gl: 14 | go run src/gl/generate/generate.go $(GL_VER) > src/gl/gl.go 15 | gofmt -w src/gl/gl.go 16 | 17 | errcheck: 18 | errcheck ./src 19 | 20 | test: 21 | go test ./src 22 | 23 | .PHONY: all wolfengo test errcheck gl 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= 2 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= 3 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= 4 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 5 | -------------------------------------------------------------------------------- /res/shaders/phongVertex.vs: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout (location = 0) in vec3 position; 4 | layout (location = 1) in vec2 texCoord; 5 | layout (location = 2) in vec3 normal; 6 | 7 | out vec2 texCoord0; 8 | out vec3 normal0; 9 | out vec3 worldPos0; 10 | 11 | uniform mat4 transform; 12 | uniform mat4 transformProjected; 13 | 14 | void main() 15 | { 16 | gl_Position = transformProjected * vec4(position, 1.0); 17 | texCoord0 = texCoord; 18 | normal0 = (transform * vec4(normal, 0.0)).xyz; 19 | worldPos0 = (transform * vec4(position, 1.0)).xyz; 20 | } -------------------------------------------------------------------------------- /src/material.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | type Material struct { 22 | texture *Texture 23 | color Vector3f 24 | specularIntensity float32 25 | specularPower float32 26 | } 27 | 28 | func NewMaterial(t *Texture) *Material { 29 | m := Material{ 30 | texture: t, 31 | color: Vector3f{1, 1, 1}, 32 | specularIntensity: 2, 33 | specularPower: 32, 34 | } 35 | return &m 36 | } 37 | -------------------------------------------------------------------------------- /src/object.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | type Object struct { 22 | K float32 23 | scale float32 24 | sizeY float32 25 | sizeX float32 26 | offsetX float32 27 | offsetY float32 28 | texMinX float32 29 | texMaxX float32 30 | texMinY float32 31 | texMaxY float32 32 | start float32 33 | 34 | size float32 35 | maxHealth int 36 | damageMin, damageMax int 37 | shootDistance float32 38 | moveSpeed float32 39 | 40 | mesh Mesh 41 | material *Material 42 | animations []*Texture 43 | } 44 | -------------------------------------------------------------------------------- /src/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | func verticesAsFloats(vertices []*Vertex) []float32 { 22 | buffer := make([]float32, 0, len(vertices)*VertexSize) 23 | 24 | for _, v := range vertices { 25 | buffer = append(buffer, v.pos.X, v.pos.Y, v.pos.Z) 26 | buffer = append(buffer, v.texCoord.X, v.texCoord.Y) 27 | buffer = append(buffer, v.normal.X, v.normal.Y, v.normal.Z) 28 | } 29 | 30 | return buffer 31 | } 32 | 33 | func removeEmptyStrings(a []string) []string { 34 | result := make([]string, 0, len(a)) 35 | for _, s := range a { 36 | if len(s) != 0 { 37 | result = append(result, s) 38 | } 39 | } 40 | return result 41 | } 42 | -------------------------------------------------------------------------------- /src/quaternion.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "math" 23 | ) 24 | 25 | type Quaternion struct { 26 | X, Y, Z, W float32 27 | } 28 | 29 | func (q Quaternion) length() float32 { 30 | return float32(math.Sqrt(float64(q.X*q.X + q.Y*q.Y + q.Z*q.Z + q.W*q.W))) 31 | } 32 | 33 | func (q Quaternion) conjugate() Quaternion { 34 | return Quaternion{-q.X, -q.Y, -q.Z, q.W} 35 | } 36 | 37 | func (q Quaternion) mul(v Vector3f) Quaternion { 38 | w := -q.X*v.X - q.Y*v.Y - q.Z*v.Z 39 | x := q.W*v.X + q.Y*v.Z - q.Z*v.Y 40 | 41 | y := q.W*v.Y + q.Z*v.X - q.X*v.Z 42 | z := q.W*v.Z + q.X*v.Y - q.Y*v.X 43 | 44 | return Quaternion{x, y, z, w} 45 | } 46 | 47 | func (q Quaternion) mulq(r Quaternion) Quaternion { 48 | w := q.W*r.W - q.X*r.X - q.Y*r.Y - q.Z*r.Z 49 | x := q.X*r.W + q.W*r.X + q.Y*r.Z - q.Z*r.Y 50 | y := q.Y*r.W + q.W*r.Y + q.Z*r.X - q.X*r.Z 51 | z := q.Z*r.W + q.W*r.Z + q.X*r.Y - q.Y*r.X 52 | 53 | return Quaternion{x, y, z, w} 54 | } 55 | -------------------------------------------------------------------------------- /src/game.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | type Game struct { 22 | level *Level 23 | isRunning bool 24 | levelNum uint 25 | 26 | // mouse look fields 27 | oldPosition Vector2f 28 | mouseLocked bool 29 | 30 | timeDelta float64 31 | } 32 | 33 | func NewGame() (*Game, error) { 34 | g := Game{} 35 | g.levelNum = 0 36 | err := g.loadNextLevel() 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return &g, err 42 | } 43 | 44 | func (g *Game) input() error { 45 | return g.level.input() 46 | } 47 | 48 | func (g *Game) update() error { 49 | if g.isRunning { 50 | return g.level.update() 51 | } 52 | return nil 53 | } 54 | 55 | func (g *Game) render() { 56 | if g.isRunning { 57 | g.level.render() 58 | } 59 | } 60 | 61 | func (g *Game) Camera() *Camera { 62 | return g.level.player.camera 63 | } 64 | 65 | func (g *Game) loadNextLevel() error { 66 | var err error 67 | g.levelNum++ 68 | g.level, err = g.NewLevel(g.levelNum) 69 | if err != nil { 70 | return err 71 | } 72 | 73 | g.isRunning = true 74 | 75 | return nil 76 | } 77 | 78 | func (g *Game) UnlockMouse() { 79 | g.mouseLocked = false 80 | } 81 | 82 | func (g *Game) LockMouse() { 83 | x, y := Window.GetCursorPos() 84 | g.oldPosition = Vector2f{float32(x), float32(y)} 85 | 86 | g.mouseLocked = true 87 | } 88 | -------------------------------------------------------------------------------- /src/transform.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import "fmt" 22 | 23 | type Transform struct { 24 | translation, rotation, scale Vector3f 25 | 26 | game *Game 27 | } 28 | 29 | func (t *Transform) String() string { 30 | return fmt.Sprintf("{trans: %s, rot: %s, scale: %s}", t.translation.String(), t.rotation.String(), t.scale.String()) 31 | } 32 | 33 | func (g *Game) NewTransform() *Transform { 34 | return &Transform{scale: Vector3f{1, 1, 1}, game: g} 35 | } 36 | 37 | func (t *Transform) getTransformation() Matrix4f { 38 | var translationMatrix, rotationMatrix, scaleMatrix Matrix4f 39 | translationMatrix.initTranslation(t.translation.X, t.translation.Y, t.translation.Z) 40 | 41 | rotationMatrix.initRotation(t.rotation.X, t.rotation.Y, t.rotation.Z) 42 | 43 | scaleMatrix.initScale(t.scale.X, t.scale.Y, t.scale.Z) 44 | 45 | return translationMatrix.mul(rotationMatrix.mul(scaleMatrix)) 46 | } 47 | 48 | func (t *Transform) getProjectedTransformation(c *Camera) Matrix4f { 49 | var projectionMatrix, cameraRotation, cameraTranslation Matrix4f 50 | 51 | transformationMatrix := t.getTransformation() 52 | projectionMatrix.initProjection(c.fov, c.width, c.height, c.zNear, c.zFar) 53 | 54 | cameraRotation.initCamera(c.forward, c.up) 55 | 56 | cameraTranslation.initTranslation(-c.pos.X, -c.pos.Y, -c.pos.Z) 57 | 58 | return projectionMatrix.mul(cameraRotation.mul(cameraTranslation.mul(transformationMatrix))) 59 | } 60 | -------------------------------------------------------------------------------- /src/camera.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | var yAxis = Vector3f{0, 1, 0} 22 | 23 | type Camera struct { 24 | pos Vector3f 25 | forward Vector3f 26 | up Vector3f 27 | 28 | mouseSensitivity float32 29 | 30 | // originally static fields in Transform 31 | zNear, zFar, width, height, fov float32 32 | } 33 | 34 | func NewCamera(pos, forward, up Vector3f, mouseSensitivity float32) *Camera { 35 | c := Camera{} 36 | c.pos = pos 37 | c.forward = forward.normalised() 38 | c.up = up.normalised() 39 | c.mouseSensitivity = mouseSensitivity 40 | w, h := Window.GetSize() 41 | c.fov, c.width, c.height, c.zNear, c.zFar = 70, float32(w), float32(h), 0.01, 1000.0 42 | 43 | return &c 44 | } 45 | 46 | func (c *Camera) mouseLook(oldPosition *Vector2f) { 47 | x, y := Window.GetCursorPos() 48 | newPosition := Vector2f{float32(x), float32(y)} 49 | deltaPos := newPosition.sub(*oldPosition) 50 | 51 | rotY := deltaPos.X != 0 52 | rotX := deltaPos.Y != 0 53 | 54 | if rotY { 55 | c.rotateY(deltaPos.X * c.mouseSensitivity) 56 | } 57 | if rotX { 58 | c.rotateX(deltaPos.Y * c.mouseSensitivity) 59 | } 60 | 61 | if rotY || rotX { 62 | *oldPosition = newPosition 63 | } 64 | } 65 | 66 | func (c *Camera) rotateY(angle float32) { 67 | Haxis := yAxis.cross(c.forward).normalised() 68 | 69 | c.forward = c.forward.rotate(angle, yAxis).normalised() 70 | 71 | c.up = c.forward.cross(Haxis).normalised() 72 | } 73 | 74 | func (c *Camera) rotateX(angle float32) { 75 | Haxis := yAxis.cross(c.forward).normalised() 76 | 77 | c.forward = c.forward.rotate(angle, Haxis).normalised() 78 | 79 | c.up = c.forward.cross(Haxis).normalised() 80 | } 81 | 82 | func (c *Camera) getLeft() Vector3f { 83 | return c.forward.cross(c.up).normalised() 84 | } 85 | 86 | func (c *Camera) getRight() Vector3f { 87 | return c.up.cross(c.forward).normalised() 88 | } 89 | 90 | func (c *Camera) move(dir Vector3f, amt float32) { 91 | c.pos = c.pos.add(dir.mulf(amt)) 92 | } 93 | -------------------------------------------------------------------------------- /src/texture.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "image" 24 | "image/color" 25 | _ "image/png" 26 | "os" 27 | 28 | "github.com/gdm85/wolfengo/src/gl" 29 | ) 30 | 31 | type Texture struct { 32 | ID uint32 33 | } 34 | 35 | type textureError struct { 36 | fileName string 37 | err error 38 | } 39 | 40 | func (te textureError) Error() string { 41 | return fmt.Sprintf("loadTexture(%s): %v", te.fileName, te.err) 42 | } 43 | 44 | func NewTexture(fileName string) (*Texture, error) { 45 | t := &Texture{} 46 | var err error 47 | t.ID, err = loadTexture(fileName) 48 | if err != nil { 49 | return nil, textureError{fileName, err} 50 | } 51 | return t, nil 52 | } 53 | 54 | func loadTexture(fileName string) (uint32, error) { 55 | imgFile, err := os.Open("./res/textures/" + fileName) 56 | if err != nil { 57 | return 0, err 58 | } 59 | 60 | imgCfg, _, err := image.DecodeConfig(imgFile) 61 | if err != nil { 62 | return 0, err 63 | } 64 | _, err = imgFile.Seek(0, 0) 65 | if err != nil { 66 | return 0, err 67 | } 68 | 69 | w, h := int32(imgCfg.Width), int32(imgCfg.Height) 70 | 71 | img, _, err := image.Decode(imgFile) 72 | if err != nil { 73 | return 0, err 74 | } 75 | 76 | buffer := make([]byte, w*h*4) 77 | index := 0 78 | for y := 0; y < int(h); y++ { 79 | for x := 0; x < int(w); x++ { 80 | pixel := img.At(x, y).(color.NRGBA) 81 | buffer[index] = pixel.R 82 | buffer[index+1] = pixel.G 83 | buffer[index+2] = pixel.B 84 | buffer[index+3] = pixel.A 85 | 86 | index += 4 87 | } 88 | } 89 | 90 | var texture uint32 91 | 92 | gl.GenTextures(1, &texture) 93 | gl.BindTexture(gl.TEXTURE_2D, texture) 94 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) 95 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) 96 | gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) 97 | gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) 98 | 99 | gl.TexImage2D( 100 | gl.TEXTURE_2D, 101 | 0, 102 | gl.RGBA8, 103 | w, 104 | h, 105 | 0, 106 | gl.RGBA, 107 | gl.UNSIGNED_BYTE, 108 | gl.Ptr(buffer)) 109 | 110 | return texture, nil 111 | } 112 | 113 | func (t *Texture) bind() { 114 | gl.BindTexture(gl.TEXTURE_2D, t.ID) 115 | } 116 | -------------------------------------------------------------------------------- /src/medkit.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | const ( 22 | pickupDistance = 0.75 23 | healAmount = 25 24 | ) 25 | 26 | var ( 27 | _defaultMedkit = Object{ 28 | K: 0.67857142857142857142857142857143, 29 | scale: 0.25, 30 | } 31 | ) 32 | 33 | func init() { 34 | _defaultMedkit.sizeY = _defaultMedkit.scale 35 | _defaultMedkit.sizeX = _defaultMedkit.sizeY / (_defaultMedkit.K * 2.5) 36 | _defaultMedkit.texMinX = -_defaultMedkit.offsetX 37 | _defaultMedkit.texMaxX = -1 - _defaultMedkit.offsetX 38 | _defaultMedkit.texMinY = -_defaultMedkit.offsetY 39 | _defaultMedkit.texMaxY = 1 - _defaultMedkit.offsetY 40 | } 41 | 42 | func (m *Object) initMedkit() error { 43 | vertices := []*Vertex{ 44 | &Vertex{Vector3f{-m.sizeX, m.start, m.start}, Vector2f{m.texMaxX, m.texMaxY}, Vector3f{0, 0, 0}}, 45 | &Vertex{Vector3f{-m.sizeX, m.sizeY, m.start}, Vector2f{m.texMaxX, m.texMinY}, Vector3f{0, 0, 0}}, 46 | &Vertex{Vector3f{m.sizeX, m.sizeY, m.start}, Vector2f{m.texMinX, m.texMinY}, Vector3f{0, 0, 0}}, 47 | &Vertex{Vector3f{m.sizeX, m.start, m.start}, Vector2f{m.texMinX, m.texMaxY}, Vector3f{0, 0, 0}}, 48 | } 49 | 50 | indices := []int32{0, 1, 2, 0, 2, 3} 51 | 52 | m.mesh = NewMesh(vertices, indices, false) 53 | 54 | t, err := NewTexture("MEDIA0.png") 55 | if err != nil { 56 | return err 57 | } 58 | 59 | m.material = NewMaterial(t) 60 | return nil 61 | } 62 | 63 | type Medkit struct { 64 | transform *Transform 65 | mesh Mesh 66 | game *Game 67 | } 68 | 69 | func (g *Game) NewMedkit(position Vector3f) *Medkit { 70 | m := Medkit{} 71 | m.game = g 72 | m.mesh = _defaultMedkit.mesh 73 | m.transform = g.NewTransform() 74 | m.transform.translation = position 75 | return &m 76 | } 77 | 78 | func (m *Medkit) update() { 79 | directionToCamera := m.game.Camera().pos.sub(m.transform.translation) 80 | 81 | angleToFaceTheCamera := AtanAndToDegrees(directionToCamera.Z / directionToCamera.X) 82 | if directionToCamera.X < 0 { 83 | angleToFaceTheCamera += 180 84 | } 85 | m.transform.rotation.Y = angleToFaceTheCamera + 90 86 | 87 | if directionToCamera.length() < pickupDistance { 88 | if m.game.level.player.health < defaultPlayer.maxHealth { 89 | m.game.level.removeMedkit(m) 90 | m.game.level.player.damage(-healAmount) 91 | } 92 | } 93 | } 94 | 95 | func (m *Medkit) render() { 96 | m.game.level.shader.updateUniforms(m.transform.getProjectedTransformation(m.game.Camera()), _defaultMedkit.material) 97 | m.mesh.draw() 98 | } 99 | -------------------------------------------------------------------------------- /src/mesh.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import "github.com/gdm85/wolfengo/src/gl" 22 | 23 | type Mesh struct { 24 | vbo, ibo uint32 25 | size int32 26 | } 27 | 28 | func NewMesh(vertices []*Vertex, indices []int32, calcNormals bool) Mesh { 29 | m := Mesh{} 30 | m.initMeshData() 31 | m.addVertices(vertices, indices, calcNormals) 32 | return m 33 | } 34 | 35 | func (m Mesh) IsEmpty() bool { 36 | return m.vbo == 0 37 | } 38 | 39 | func (m *Mesh) initMeshData() { 40 | gl.GenBuffers(1, &m.vbo) 41 | gl.GenBuffers(1, &m.ibo) 42 | m.size = 0 43 | } 44 | 45 | func (m *Mesh) addVertices(vertices []*Vertex, indices []int32, calcNormals bool) { 46 | if calcNormals { 47 | m.calcNormals(vertices, indices) 48 | } 49 | 50 | m.size = int32(len(indices)) 51 | fb := verticesAsFloats(vertices) 52 | 53 | gl.BindBuffer(gl.ARRAY_BUFFER, m.vbo) 54 | gl.BufferData(gl.ARRAY_BUFFER, len(fb)*4, gl.Ptr(fb), gl.STATIC_DRAW) 55 | 56 | gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.ibo) 57 | gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices)*4, gl.Ptr(indices), gl.STATIC_DRAW) 58 | } 59 | 60 | func (m Mesh) draw() { 61 | gl.EnableVertexAttribArray(0) 62 | gl.EnableVertexAttribArray(1) 63 | gl.EnableVertexAttribArray(2) 64 | 65 | if m.vbo == 0 { 66 | panic("attempt to set array buffer with VBO=0") 67 | } 68 | 69 | gl.BindBuffer(gl.ARRAY_BUFFER, m.vbo) 70 | gl.VertexAttribPointer(0, 3, gl.FLOAT, false, VertexSize*4, gl.PtrOffset(0)) 71 | gl.VertexAttribPointer(1, 2, gl.FLOAT, false, VertexSize*4, gl.PtrOffset(12)) 72 | gl.VertexAttribPointer(2, 3, gl.FLOAT, false, VertexSize*4, gl.PtrOffset(20)) 73 | 74 | if m.ibo == 0 { 75 | panic("attempt to set element array buffer with IBO=0") 76 | } 77 | 78 | gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.ibo) 79 | 80 | if m.size == 0 { 81 | panic("attempt to draw elements with mesh size = 0") 82 | } 83 | 84 | gl.DrawElements(gl.TRIANGLES, m.size, gl.UNSIGNED_INT, gl.PtrOffset(0)) 85 | 86 | gl.DisableVertexAttribArray(0) 87 | gl.DisableVertexAttribArray(1) 88 | gl.DisableVertexAttribArray(2) 89 | } 90 | 91 | func (m Mesh) calcNormals(vertices []*Vertex, indices []int32) { 92 | for i := 0; i < len(indices); i += 3 { 93 | i0, i1, i2 := indices[i], indices[i+1], indices[i+2] 94 | 95 | v1 := vertices[i1].pos.sub(vertices[i0].pos) 96 | v2 := vertices[i2].pos.sub(vertices[i0].pos) 97 | 98 | normal := v1.cross(v2).normalised() 99 | 100 | vertices[i0].normal = vertices[i0].normal.add(normal) 101 | vertices[i1].normal = vertices[i1].normal.add(normal) 102 | vertices[i2].normal = vertices[i2].normal.add(normal) 103 | } 104 | 105 | for _, v := range vertices { 106 | v.normal = v.normal.normalised() 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/gl/gl.go: -------------------------------------------------------------------------------- 1 | // Code generated by src/gl/generate/generate.go DO NOT EDIT 2 | package gl 3 | 4 | import "github.com/go-gl/gl/v2.1/gl" 5 | 6 | const ( 7 | RequestedContextVersionMajor = 2 8 | RequestedContextVersionMinor = 1 9 | ) 10 | 11 | const ( 12 | VERTEX_SHADER = gl.VERTEX_SHADER 13 | FRAGMENT_SHADER = gl.FRAGMENT_SHADER 14 | DEBUG_SEVERITY_HIGH = gl.DEBUG_SEVERITY_HIGH 15 | VERSION = gl.VERSION 16 | DEBUG_OUTPUT = gl.DEBUG_OUTPUT 17 | CW = gl.CW 18 | BACK = gl.BACK 19 | CULL_FACE = gl.CULL_FACE 20 | DEPTH_TEST = gl.DEPTH_TEST 21 | BLEND = gl.BLEND 22 | SRC_ALPHA = gl.SRC_ALPHA 23 | ONE_MINUS_SRC_ALPHA = gl.ONE_MINUS_SRC_ALPHA 24 | DEPTH_CLAMP = gl.DEPTH_CLAMP 25 | TEXTURE_2D = gl.TEXTURE_2D 26 | COLOR_BUFFER_BIT = gl.COLOR_BUFFER_BIT 27 | DEPTH_BUFFER_BIT = gl.DEPTH_BUFFER_BIT 28 | ARRAY_BUFFER = gl.ARRAY_BUFFER 29 | ELEMENT_ARRAY_BUFFER = gl.ELEMENT_ARRAY_BUFFER 30 | STATIC_DRAW = gl.STATIC_DRAW 31 | FLOAT = gl.FLOAT 32 | TRIANGLES = gl.TRIANGLES 33 | UNSIGNED_INT = gl.UNSIGNED_INT 34 | INFO_LOG_LENGTH = gl.INFO_LOG_LENGTH 35 | LINK_STATUS = gl.LINK_STATUS 36 | FALSE = gl.FALSE 37 | VALIDATE_STATUS = gl.VALIDATE_STATUS 38 | COMPILE_STATUS = gl.COMPILE_STATUS 39 | TEXTURE_MIN_FILTER = gl.TEXTURE_MIN_FILTER 40 | TEXTURE_WRAP_T = gl.TEXTURE_WRAP_T 41 | REPEAT = gl.REPEAT 42 | TEXTURE_WRAP_S = gl.TEXTURE_WRAP_S 43 | NEAREST = gl.NEAREST 44 | TEXTURE_MAG_FILTER = gl.TEXTURE_MAG_FILTER 45 | RGBA8 = gl.RGBA8 46 | RGBA = gl.RGBA 47 | UNSIGNED_BYTE = gl.UNSIGNED_BYTE 48 | ) 49 | 50 | var ( 51 | DrawElements = gl.DrawElements 52 | Init = gl.Init 53 | GoStr = gl.GoStr 54 | GetString = gl.GetString 55 | DebugMessageCallback = gl.DebugMessageCallback 56 | Enable = gl.Enable 57 | ClearColor = gl.ClearColor 58 | FrontFace = gl.FrontFace 59 | CullFace = gl.CullFace 60 | BlendFunc = gl.BlendFunc 61 | Clear = gl.Clear 62 | GenBuffers = gl.GenBuffers 63 | BindBuffer = gl.BindBuffer 64 | BufferData = gl.BufferData 65 | Ptr = gl.Ptr 66 | EnableVertexAttribArray = gl.EnableVertexAttribArray 67 | VertexAttribPointer = gl.VertexAttribPointer 68 | PtrOffset = gl.PtrOffset 69 | DisableVertexAttribArray = gl.DisableVertexAttribArray 70 | CreateProgram = gl.CreateProgram 71 | UseProgram = gl.UseProgram 72 | Strs = gl.Strs 73 | GetUniformLocation = gl.GetUniformLocation 74 | GetProgramiv = gl.GetProgramiv 75 | GetProgramInfoLog = gl.GetProgramInfoLog 76 | GetShaderiv = gl.GetShaderiv 77 | GetShaderInfoLog = gl.GetShaderInfoLog 78 | Str = gl.Str 79 | LinkProgram = gl.LinkProgram 80 | ValidateProgram = gl.ValidateProgram 81 | CreateShader = gl.CreateShader 82 | ShaderSource = gl.ShaderSource 83 | CompileShader = gl.CompileShader 84 | AttachShader = gl.AttachShader 85 | Uniform3f = gl.Uniform3f 86 | UniformMatrix4fv = gl.UniformMatrix4fv 87 | BindTexture = gl.BindTexture 88 | GenTextures = gl.GenTextures 89 | TexParameteri = gl.TexParameteri 90 | TexParameterf = gl.TexParameterf 91 | TexImage2D = gl.TexImage2D 92 | GenVertexArrays = gl.GenVertexArrays 93 | BindVertexArray = gl.BindVertexArray 94 | ) 95 | -------------------------------------------------------------------------------- /maps/level1.map: -------------------------------------------------------------------------------- 1 | wall4 {1.00,0.75,0.00,0.25} 2 | wall2 {1.00,0.75,0.75,1.00} 3 | wall5 {0.50,0.25,0.50,0.75} 4 | wall3 {0.25,0.00,0.00,0.25} 5 | wall1 {1.00,0.75,0.25,0.50} 6 | lengthmap 032 7 | MAP: 8 | 9 | 10 | 1111111111111111111 11 | 111111111111111 1 12 | 11 1 341 13 | 111 1111 33 33333 1 541 14 | 111 1111 33 33333 1 341 15 | 111 1111 33 33 11 1 16 | 111111113333 33 1111 17 | 111 11113333 33 33 18 | 111 33 33 33 19 | 33 33 33 3 20 | 33 33 33 3 3333 21 | 33333333333333333333 22 | 33333333333333333333 23 | 33 3 3333 24 | 33 33333 3333 25 | 33 33333 4334 26 | 33333 3333 3333 27 | 3333333333 4334 28 | 33333 3333 3333 29 | 3333 30 | 3333 31 | 3333 32 | 3333 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | PLANES: 41 | 42 | 43 | 1111111111111111111 44 | 111111111111111 1 45 | 11 1 222 46 | 111 1111 22 22222 1 222 47 | 111 1111 22 22222 1 222 48 | 111 1111 22 22 11 1 49 | 111111112222 22 1111 50 | 111 11112222 22 22 51 | 111 22 22 22 52 | 22 22 22 2 53 | 22 22 22 2 2222 54 | 22222222222222222222 55 | 22222222222222222222 56 | 22 2 2222 57 | 22 22222 2222 58 | 22 22222 2222 59 | 22222 2222 2222 60 | 2222222222 2222 61 | 22222 2222 2222 62 | 2222 63 | 2222 64 | 2222 65 | 2222 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | SPECIALS: 74 | 75 | 76 | 77 | 78 | 79 | m X 80 | e 81 | 82 | m d 83 | e A 84 | 85 | 86 | d 87 | e 88 | 89 | d 90 | m 91 | e 92 | e 93 | d 94 | m 95 | 96 | e 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /maps/levelTest.map: -------------------------------------------------------------------------------- 1 | wall1 {1.00,0.75,0.75,1.00} 2 | wall2 {0.25,0.00,0.00,0.25} 3 | lengthmap 032 4 | MAP: 5 | 6 | 7 | 8 | 9 | 10 | 22222222 11 | 222222222 12 | 222 22222 2222 13 | 222 22 22 2222 14 | 2222 22 22 2222 15 | 2222 22 22 2222 16 | 22 22 22 2222 17 | 22 22 22 2 2222 18 | 222 22 22 22222 2222 19 | 222 22 22 2222222222 20 | 2222 22 22 222 2222 21 | 2222 22 22 222 2222 22 | 222 22222 2222 23 | 222 22222 22222222222 24 | 222 222222222222 2 25 | 22 222222222 2 26 | 222222 2 222 27 | 2222222222 2 28 | 2222222222 222222222 29 | 222222222 222222222 30 | 22222222 222222222 31 | 222222 222222222 32 | 22222 222222222 33 | 22222222 34 | 35 | 36 | 37 | PLANES: 38 | 39 | 40 | 41 | 42 | 43 | 11111111 44 | 111111111 45 | 111 11111 1111 46 | 111 11 11 1111 47 | 1111 11 11 1111 48 | 1111 11 11 1111 49 | 11 11 11 1111 50 | 11 11 11 1 1111 51 | 111 11 11 11111 1111 52 | 111 11 11 1111111111 53 | 1111 11 11 111 1111 54 | 1111 11 11 111 1111 55 | 111 11111 1111 56 | 111 11111 11111111111 57 | 111 111111111111 1 58 | 11 111111111 1 59 | 111111 1 111 60 | 1111111111 1 61 | 1111111111 111111111 62 | 111111111 111111111 63 | 11111111 111111111 64 | 111111 111111111 65 | 11111 111111111 66 | 11111111 67 | 68 | 69 | 70 | SPECIALS: 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | m 79 | 80 | 81 | e 82 | 83 | e d m 84 | 85 | d 86 | e e 87 | 88 | m 89 | m 90 | d 91 | 92 | 93 | d 94 | m 95 | A 96 | e 97 | 98 | 99 | m 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/vectors.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "math" 24 | ) 25 | 26 | const VertexSize = 8 27 | 28 | type Vector3f struct { 29 | X, Y, Z float32 30 | } 31 | 32 | func (v *Vector3f) String() string { 33 | return fmt.Sprintf("[X: %.3f, Y: %.3f, Z: %.3f]", v.X, v.Y, v.Z) 34 | } 35 | 36 | type Vector2f struct { 37 | X, Y float32 38 | } 39 | 40 | type Vertex struct { 41 | pos Vector3f 42 | texCoord Vector2f 43 | normal Vector3f 44 | } 45 | 46 | func (v Vector2f) sub(s Vector2f) Vector2f { 47 | return Vector2f{v.X - s.X, v.Y - s.Y} 48 | } 49 | 50 | func (v Vector2f) add(s Vector2f) Vector2f { 51 | return Vector2f{v.X + s.X, v.Y + s.Y} 52 | } 53 | 54 | func (v Vector2f) mulf(f float32) Vector2f { 55 | return Vector2f{v.X * f, v.Y * f} 56 | } 57 | 58 | func (v Vector2f) length() float32 { 59 | return float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y))) 60 | } 61 | 62 | func (v Vector2f) rotate(angle float32) Vector2f { 63 | rad := float64(toRadians(angle)) 64 | 65 | cos := float32(math.Cos(rad)) 66 | sin := float32(math.Sin(rad)) 67 | 68 | return Vector2f{v.X*cos - v.Y*sin, v.X*sin + v.Y*cos} 69 | } 70 | 71 | func (v Vector2f) mul(s Vector2f) Vector2f { 72 | return Vector2f{v.X * s.X, v.Y * s.Y} 73 | } 74 | 75 | func (v Vector2f) normalised() Vector2f { 76 | length := v.length() 77 | 78 | return Vector2f{v.X / length, v.Y / length} 79 | } 80 | 81 | func (v Vector3f) sub(s Vector3f) Vector3f { 82 | return Vector3f{v.X - s.X, v.Y - s.Y, v.Z - s.Z} 83 | } 84 | 85 | func (v Vector3f) add(s Vector3f) Vector3f { 86 | return Vector3f{v.X + s.X, v.Y + s.Y, v.Z + s.Z} 87 | } 88 | 89 | func (v Vector3f) mulf(f float32) Vector3f { 90 | return Vector3f{v.X * f, v.Y * f, v.Z * f} 91 | } 92 | 93 | func (v Vector3f) divf(f float32) Vector3f { 94 | return Vector3f{v.X / f, v.Y / f, v.Z / f} 95 | } 96 | 97 | func (v Vector3f) cross(s Vector3f) Vector3f { 98 | x := v.Y*s.Z - v.Z*s.Y 99 | y := v.Z*s.X - v.X*s.Z 100 | z := v.X*s.Y - v.Y*s.X 101 | 102 | return Vector3f{x, y, z} 103 | } 104 | 105 | func (v Vector3f) mul(s Vector3f) Vector3f { 106 | return Vector3f{v.X * s.X, v.Y * s.Y, v.Z * s.Z} 107 | } 108 | 109 | func (v Vector3f) length() float32 { 110 | return float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y + v.Z*v.Z))) 111 | } 112 | 113 | func (v Vector3f) normalised() Vector3f { 114 | length := v.length() 115 | 116 | return Vector3f{v.X / length, v.Y / length, v.Z / length} 117 | } 118 | 119 | func toRadians(degrees float32) float32 { 120 | return (degrees * math.Pi) / 180 121 | } 122 | 123 | func AtanAndToDegrees(radians float32) float32 { 124 | return float32((math.Atan(float64(radians)) * 180) / math.Pi) 125 | } 126 | 127 | func (v Vector3f) rotate(angle float32, axis Vector3f) Vector3f { 128 | radians := float64(toRadians(angle / 2)) 129 | sinHalfAngle := float32(math.Sin(radians)) 130 | cosHalfAngle := float32(math.Cos(radians)) 131 | 132 | rX := axis.X * sinHalfAngle 133 | rY := axis.Y * sinHalfAngle 134 | rZ := axis.Z * sinHalfAngle 135 | rW := cosHalfAngle 136 | 137 | rotation := Quaternion{rX, rY, rZ, rW} 138 | conjugate := rotation.conjugate() 139 | 140 | w := rotation.mul(v).mulq(conjugate) 141 | 142 | return Vector3f{w.X, w.Y, w.Z} 143 | } 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WolfenGo 2 | 3 | This is a clone of Wolfenstein3D written in Go, based on [Wolfenstein3DClone](https://github.com/BennyQBD/Wolfenstein3DClone) and licensed under [GNU/GPLv2](./LICENSE). 4 | 5 | Pull requests are welcome. 6 | 7 | ## Plan 8 | - [x] initial conversion 9 | - [x] fix remaining bugs 10 | - [ ] add audio effects 11 | 12 | # Build Dependencies 13 | 14 | WolfenGo uses glfw C bindings, which in turn need some Linux userland headers to be installed. Example of dependencies installation on a Debian-based system: 15 | ``` 16 | apt-get install libgl1-mesa-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libxxf86vm-dev 17 | ``` 18 | 19 | # Building 20 | 21 | ``` 22 | make 23 | ``` 24 | 25 | Then you can run: 26 | ``` 27 | bin/wolfengo 28 | ``` 29 | 30 | There are some constants in `main.go` that can be toggled to enable further debugging/experimentation. 31 | 32 | # Controls 33 | 34 | Use `W`,`A`,`S`,`D` to move the player around and `E` to open doors; by clicking in the game window you will enable free mouse look, that can be disabled with `ESC`. 35 | 36 | Pressing `Q` causes the game to quit, as it does closing the game window itself. 37 | 38 | # History 39 | 40 | Aside from some dead/unused code that I have dropped and bugs inadvertently introduced in the porting process, this is my ([gdm85](https://github.com/gdm85)) literal conversion of the [Java Wolfenstein3D clone by BennyQBD](https://github.com/BennyQBD/Wolfenstein3DClone); feel free to spin up the Java original version to check how identical and indistinguishable the two are. 41 | 42 | Notable differences: 43 | * map format has been changed, see relative section 44 | * enabled VSync 45 | * although extra shaders are included, they are not used by default in any way 46 | 47 | Although [mathgl](https://github.com/go-gl/mathgl) could have been used for the 3D math/raycast operations, I preferred to keep the simpler original structures. 48 | 49 | Some lessons learnt during the porting process: 50 | * after 10 years I last experimented with OpenGL debugging, it's still hard. [apitrace](https://github.com/apitrace/apitrace) helped a lot 51 | * Go's `int` is 64bit on 64bit platforms, a visual inspection doesn't tip off this easily and one could skip the fact that OpenGL needs to know the proper size (4 or 8 bytes) 52 | * a minus sign here and there can screw up **a lot** of projections/translations 53 | 54 | Development started on 4 January and completed with the first release on Github on 24th January with squashed/cleaned up commits. 55 | See also [my blog post about WolfenGo initial release](https://medium.com/where-do-we-go-now/wolfengo-a-wolfenstein-3d-clone-in-go-6872af12469d) for extras about the development/porting process. 56 | 57 | # Map format 58 | 59 | The map format has been changed as well to be easier to edit via text editors (although the new format it's far from being final). 60 | 61 | Some extensions have been added for other items (FPS lore quiz: where have you seen this map format already?) 62 | 63 | The first lines of the map define walls: 64 | ``` 65 | wall1 {1.00,0.75,0.75,1.00} 66 | wall2 {0.25,0.00,0.00,0.25} 67 | ``` 68 | 69 | The values in curly braces are texture coordinates from the tileset [WolfCollection.png](./res/textures/WolfCollection.png). 70 | After that follows the `lengthmap` definition to indicate map size: 71 | ``` 72 | lengthmap 032 73 | ``` 74 | The value `32` means that this is a 32x32 map. 75 | 76 | Subsequently there are the `MAP`, `PLANES` and `SPECIALS` sections, each of them describing with `n = 32` lines of `n = 32` characters either a wall value, 77 | a floor/ceiling value or a special item. Wall characters start at `1` and (unfortunately) right now can as well go above `9`. 78 | 79 | The special items that are currently supported are: 80 | * `m` to indicate a small medkit 81 | * `e` to indicate an enemy 82 | * `d` to indicate a door 83 | * `A` to indicate player start position 84 | * `X` to indicate level exit 85 | 86 | # Thanks 87 | 88 | Obviously thanks to BennyQBD for the initial clone Java sources and also to https://github.com/go-gl/gl which - although not easy to master - is indeed in a good status for usage in Go OpenGL projects. 89 | -------------------------------------------------------------------------------- /res/shaders/phongFragment.fs: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | const int MAX_POINT_LIGHTS = 4; 4 | const int MAX_SPOT_LIGHTS = 4; 5 | 6 | in vec2 texCoord0; 7 | in vec3 normal0; 8 | in vec3 worldPos0; 9 | 10 | out vec4 fragColor; 11 | 12 | struct BaseLight 13 | { 14 | vec3 color; 15 | float intensity; 16 | }; 17 | 18 | struct DirectionalLight 19 | { 20 | BaseLight base; 21 | vec3 direction; 22 | }; 23 | 24 | struct Attenuation 25 | { 26 | float constant; 27 | float linear; 28 | float exponent; 29 | }; 30 | 31 | struct PointLight 32 | { 33 | BaseLight base; 34 | Attenuation atten; 35 | vec3 position; 36 | float range; 37 | }; 38 | 39 | struct SpotLight 40 | { 41 | PointLight pointLight; 42 | vec3 direction; 43 | float cutoff; 44 | }; 45 | 46 | uniform vec3 baseColor; 47 | uniform vec3 eyePos; 48 | uniform vec3 ambientLight; 49 | uniform sampler2D sampler; 50 | 51 | uniform float specularIntensity; 52 | uniform float specularPower; 53 | 54 | uniform DirectionalLight directionalLight; 55 | uniform PointLight pointLights[MAX_POINT_LIGHTS]; 56 | uniform SpotLight spotLights[MAX_SPOT_LIGHTS]; 57 | 58 | vec4 calcLight(BaseLight base, vec3 direction, vec3 normal) 59 | { 60 | float diffuseFactor = dot(normal, -direction); 61 | 62 | vec4 diffuseColor = vec4(0,0,0,0); 63 | vec4 specularColor = vec4(0,0,0,0); 64 | 65 | if(diffuseFactor > 0) 66 | { 67 | diffuseColor = vec4(base.color, 1.0) * base.intensity * diffuseFactor; 68 | 69 | vec3 directionToEye = normalize(eyePos - worldPos0); 70 | vec3 reflectDirection = normalize(reflect(direction, normal)); 71 | 72 | float specularFactor = dot(directionToEye, reflectDirection); 73 | specularFactor = pow(specularFactor, specularPower); 74 | 75 | if(specularFactor > 0) 76 | { 77 | specularColor = vec4(base.color, 1.0) * specularIntensity * specularFactor; 78 | } 79 | } 80 | 81 | return diffuseColor + specularColor; 82 | } 83 | 84 | vec4 calcDirectionalLight(DirectionalLight directionalLight, vec3 normal) 85 | { 86 | return calcLight(directionalLight.base, -directionalLight.direction, normal); 87 | } 88 | 89 | vec4 calcPointLight(PointLight pointLight, vec3 normal) 90 | { 91 | vec3 lightDirection = worldPos0 - pointLight.position; 92 | float distanceToPoint = length(lightDirection); 93 | 94 | if(distanceToPoint > pointLight.range) 95 | return vec4(0,0,0,0); 96 | 97 | lightDirection = normalize(lightDirection); 98 | 99 | vec4 color = calcLight(pointLight.base, lightDirection, normal); 100 | 101 | float attenuation = pointLight.atten.constant + 102 | pointLight.atten.linear * distanceToPoint + 103 | pointLight.atten.exponent * distanceToPoint * distanceToPoint + 104 | 0.0001; 105 | 106 | return color / attenuation; 107 | } 108 | 109 | vec4 calcSpotLight(SpotLight spotLight, vec3 normal) 110 | { 111 | vec3 lightDirection = normalize(worldPos0 - spotLight.pointLight.position); 112 | float spotFactor = dot(lightDirection, spotLight.direction); 113 | 114 | vec4 color = vec4(0,0,0,0); 115 | 116 | if(spotFactor > spotLight.cutoff) 117 | { 118 | color = calcPointLight(spotLight.pointLight, normal) * 119 | (1.0 - (1.0 - spotFactor)/(1.0 - spotLight.cutoff)); 120 | } 121 | 122 | return color; 123 | } 124 | 125 | void main() 126 | { 127 | vec4 totalLight = vec4(ambientLight,1); 128 | vec4 color = vec4(baseColor, 1); 129 | vec4 textureColor = texture(sampler, texCoord0.xy); 130 | 131 | if(textureColor != vec4(0,0,0,0)) 132 | color *= textureColor; 133 | 134 | vec3 normal = normalize(normal0); 135 | 136 | totalLight += calcDirectionalLight(directionalLight, normal); 137 | 138 | for(int i = 0; i < MAX_POINT_LIGHTS; i++) 139 | if(pointLights[i].base.intensity > 0) 140 | totalLight += calcPointLight(pointLights[i], normal); 141 | 142 | for(int i = 0; i < MAX_SPOT_LIGHTS; i++) 143 | if(spotLights[i].pointLight.base.intensity > 0) 144 | totalLight += calcSpotLight(spotLights[i], normal); 145 | 146 | fragColor = color * totalLight; 147 | } -------------------------------------------------------------------------------- /src/shader.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "errors" 23 | "fmt" 24 | "io/ioutil" 25 | "strings" 26 | 27 | "github.com/gdm85/wolfengo/src/gl" 28 | ) 29 | 30 | type Shader struct { 31 | program uint32 32 | uniforms map[string]int32 33 | } 34 | 35 | func NewShader(withUpdateUniforms bool) (*Shader, error) { 36 | s := &Shader{} 37 | s.program = gl.CreateProgram() 38 | if s.program == 0 { 39 | return nil, errors.New("shader creation failed: could not find valid memory location when creating program") 40 | } 41 | 42 | if withUpdateUniforms { 43 | s.uniforms = make(map[string]int32, 0) 44 | } 45 | 46 | return s, nil 47 | } 48 | 49 | func (s *Shader) bind() { 50 | gl.UseProgram(s.program) 51 | } 52 | 53 | func (s *Shader) addUniform(uniformName string) error { 54 | csource, free := gl.Strs(uniformName + "\000") 55 | defer free() 56 | uniformLocation := gl.GetUniformLocation(s.program, *csource) 57 | 58 | if uniformLocation == -1 { 59 | return fmt.Errorf("could not find uniform: %s", uniformName) 60 | } 61 | 62 | s.uniforms[uniformName] = uniformLocation 63 | return nil 64 | } 65 | 66 | func (s *Shader) getProgramInfoLog(context string) error { 67 | var logLength int32 68 | gl.GetProgramiv(s.program, gl.INFO_LOG_LENGTH, &logLength) 69 | 70 | log := strings.Repeat("\x00", int(logLength+1)) 71 | gl.GetProgramInfoLog(s.program, logLength, nil, gl.Str(log)) 72 | 73 | return fmt.Errorf("%s: %s", context, log) 74 | } 75 | 76 | func (s *Shader) getShaderInfoLog(shader uint32, context string) error { 77 | var logLength int32 78 | gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength) 79 | 80 | log := strings.Repeat("\x00", int(logLength+1)) 81 | gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log)) 82 | 83 | return fmt.Errorf("%s: %s", context, log) 84 | } 85 | 86 | func (s *Shader) compile() error { 87 | var vao uint32 88 | gl.GenVertexArrays(1, &vao) 89 | gl.BindVertexArray(vao) 90 | 91 | gl.LinkProgram(s.program) 92 | var result int32 93 | gl.GetProgramiv(s.program, gl.LINK_STATUS, &result) 94 | if result == gl.FALSE { 95 | return s.getProgramInfoLog("shader linking error") 96 | } 97 | gl.ValidateProgram(s.program) 98 | gl.GetProgramiv(s.program, gl.VALIDATE_STATUS, &result) 99 | if result == gl.FALSE { 100 | return s.getProgramInfoLog("shader validation error") 101 | } 102 | 103 | return nil 104 | } 105 | 106 | func (s *Shader) addProgram(text string, typ uint32) error { 107 | shader := gl.CreateShader(typ) 108 | if shader == 0 { 109 | return errors.New("could not find valid memory location when adding shader") 110 | } 111 | cStr, free := gl.Strs(text + "\000") 112 | defer free() 113 | gl.ShaderSource(shader, 1, cStr, nil) 114 | gl.CompileShader(shader) 115 | 116 | var result int32 117 | gl.GetShaderiv(shader, gl.COMPILE_STATUS, &result) 118 | if result == gl.FALSE { 119 | return s.getShaderInfoLog(shader, "shader compilation error") 120 | } 121 | 122 | gl.AttachShader(s.program, shader) 123 | return nil 124 | } 125 | 126 | func (s *Shader) addProgramFromFile(fileName string, typ uint32) error { 127 | data, err := ioutil.ReadFile("./res/shaders/" + fileName) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | return s.addProgram(string(data), typ) 133 | } 134 | 135 | func (s *Shader) setUniform(uniformName string, value Vector3f) { 136 | gl.Uniform3f(s.uniforms[uniformName], value.X, value.Y, value.Z) 137 | } 138 | 139 | func (s *Shader) setUniformM(uniformName string, value Matrix4f) { 140 | floats := value.asArray() 141 | 142 | gl.UniformMatrix4fv(s.uniforms[uniformName], 1, true, &floats[0]) 143 | } 144 | 145 | func (s *Shader) updateUniforms(projectedMatrix Matrix4f, material *Material) { 146 | if s.uniforms == nil { 147 | return 148 | } 149 | 150 | if material.texture != nil { 151 | material.texture.bind() 152 | } else { 153 | gl.BindTexture(gl.TEXTURE_2D, 0) 154 | } 155 | 156 | s.setUniformM("transform", projectedMatrix) 157 | s.setUniform("color", material.color) 158 | } 159 | -------------------------------------------------------------------------------- /src/gl/generate/generate.go: -------------------------------------------------------------------------------- 1 | // +build generate 2 | /* 3 | WolfenGo - https://github.com/gdm85/wolfengo 4 | Copyright (C) 2016~2019 gdm85 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, Inc., 18 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "os" 26 | "strings" 27 | ) 28 | 29 | func main() { 30 | glVer := os.Args[1] 31 | 32 | parts := strings.Split(glVer, ".") 33 | major, minor := parts[0][1:], parts[1] 34 | 35 | // remove '-core' 36 | minor = strings.Replace(minor, "-core", "", 1) 37 | 38 | fmt.Printf(`// Code generated by src/gl/generate/generate.go DO NOT EDIT 39 | package gl 40 | 41 | import "github.com/go-gl/gl/%s/gl" 42 | 43 | const ( 44 | RequestedContextVersionMajor = %s 45 | RequestedContextVersionMinor = %s 46 | ) 47 | 48 | const ( 49 | VERTEX_SHADER = gl.VERTEX_SHADER 50 | FRAGMENT_SHADER = gl.FRAGMENT_SHADER 51 | DEBUG_SEVERITY_HIGH = gl.DEBUG_SEVERITY_HIGH 52 | VERSION = gl.VERSION 53 | DEBUG_OUTPUT = gl.DEBUG_OUTPUT 54 | CW = gl.CW 55 | BACK = gl.BACK 56 | CULL_FACE = gl.CULL_FACE 57 | DEPTH_TEST = gl.DEPTH_TEST 58 | BLEND = gl.BLEND 59 | SRC_ALPHA = gl.SRC_ALPHA 60 | ONE_MINUS_SRC_ALPHA = gl.ONE_MINUS_SRC_ALPHA 61 | DEPTH_CLAMP = gl.DEPTH_CLAMP 62 | TEXTURE_2D = gl.TEXTURE_2D 63 | COLOR_BUFFER_BIT = gl.COLOR_BUFFER_BIT 64 | DEPTH_BUFFER_BIT = gl.DEPTH_BUFFER_BIT 65 | ARRAY_BUFFER = gl.ARRAY_BUFFER 66 | ELEMENT_ARRAY_BUFFER = gl.ELEMENT_ARRAY_BUFFER 67 | STATIC_DRAW = gl.STATIC_DRAW 68 | FLOAT = gl.FLOAT 69 | TRIANGLES = gl.TRIANGLES 70 | UNSIGNED_INT = gl.UNSIGNED_INT 71 | INFO_LOG_LENGTH = gl.INFO_LOG_LENGTH 72 | LINK_STATUS = gl.LINK_STATUS 73 | FALSE = gl.FALSE 74 | VALIDATE_STATUS = gl.VALIDATE_STATUS 75 | COMPILE_STATUS = gl.COMPILE_STATUS 76 | TEXTURE_MIN_FILTER = gl.TEXTURE_MIN_FILTER 77 | TEXTURE_WRAP_T = gl.TEXTURE_WRAP_T 78 | REPEAT = gl.REPEAT 79 | TEXTURE_WRAP_S = gl.TEXTURE_WRAP_S 80 | NEAREST = gl.NEAREST 81 | TEXTURE_MAG_FILTER = gl.TEXTURE_MAG_FILTER 82 | RGBA8 = gl.RGBA8 83 | RGBA = gl.RGBA 84 | UNSIGNED_BYTE = gl.UNSIGNED_BYTE 85 | ) 86 | 87 | var ( 88 | DrawElements = gl.DrawElements 89 | Init = gl.Init 90 | GoStr = gl.GoStr 91 | GetString = gl.GetString 92 | DebugMessageCallback = gl.DebugMessageCallback 93 | Enable = gl.Enable 94 | ClearColor = gl.ClearColor 95 | FrontFace = gl.FrontFace 96 | CullFace = gl.CullFace 97 | BlendFunc = gl.BlendFunc 98 | Clear = gl.Clear 99 | GenBuffers = gl.GenBuffers 100 | BindBuffer = gl.BindBuffer 101 | BufferData = gl.BufferData 102 | Ptr = gl.Ptr 103 | EnableVertexAttribArray = gl.EnableVertexAttribArray 104 | VertexAttribPointer = gl.VertexAttribPointer 105 | PtrOffset = gl.PtrOffset 106 | DisableVertexAttribArray = gl.DisableVertexAttribArray 107 | CreateProgram = gl.CreateProgram 108 | UseProgram = gl.UseProgram 109 | Strs = gl.Strs 110 | GetUniformLocation = gl.GetUniformLocation 111 | GetProgramiv = gl.GetProgramiv 112 | GetProgramInfoLog = gl.GetProgramInfoLog 113 | GetShaderiv = gl.GetShaderiv 114 | GetShaderInfoLog = gl.GetShaderInfoLog 115 | Str = gl.Str 116 | LinkProgram = gl.LinkProgram 117 | ValidateProgram = gl.ValidateProgram 118 | CreateShader = gl.CreateShader 119 | ShaderSource = gl.ShaderSource 120 | CompileShader = gl.CompileShader 121 | AttachShader = gl.AttachShader 122 | Uniform3f = gl.Uniform3f 123 | UniformMatrix4fv = gl.UniformMatrix4fv 124 | BindTexture = gl.BindTexture 125 | GenTextures = gl.GenTextures 126 | TexParameteri = gl.TexParameteri 127 | TexParameterf = gl.TexParameterf 128 | TexImage2D = gl.TexImage2D 129 | GenVertexArrays = gl.GenVertexArrays 130 | BindVertexArray = gl.BindVertexArray 131 | ) 132 | `, glVer, major, minor) 133 | } 134 | -------------------------------------------------------------------------------- /src/matrix.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "math" 24 | ) 25 | 26 | type Matrix4f [4][4]float32 27 | 28 | func (m *Matrix4f) initIdentity() { 29 | m[0][0] = 1 30 | m[0][1] = 0 31 | m[0][2] = 0 32 | m[0][3] = 0 33 | 34 | m[1][0] = 0 35 | m[1][1] = 1 36 | m[1][2] = 0 37 | m[1][3] = 0 38 | 39 | m[2][0] = 0 40 | m[2][1] = 0 41 | m[2][2] = 1 42 | m[2][3] = 0 43 | 44 | m[3][0] = 0 45 | m[3][1] = 0 46 | m[3][2] = 0 47 | m[3][3] = 1 48 | return 49 | } 50 | 51 | func (m *Matrix4f) initTranslation(x, y, z float32) { 52 | m[0][0] = 1 53 | m[0][1] = 0 54 | m[0][2] = 0 55 | m[0][3] = x 56 | 57 | m[1][0] = 0 58 | m[1][1] = 1 59 | m[1][2] = 0 60 | m[1][3] = y 61 | 62 | m[2][0] = 0 63 | m[2][1] = 0 64 | m[2][2] = 1 65 | m[2][3] = z 66 | 67 | m[3][0] = 0 68 | m[3][1] = 0 69 | m[3][2] = 0 70 | m[3][3] = 1 71 | } 72 | 73 | func (m *Matrix4f) initRotation(_x, _y, _z float32) { 74 | var rx, ry, rz Matrix4f 75 | 76 | x, y, z := float64(toRadians(_x)), float64(toRadians(_y)), float64(toRadians(_z)) 77 | 78 | rz[0][0] = float32(math.Cos(z)) 79 | rz[0][1] = -float32(math.Sin(z)) 80 | rz[0][2] = 0 81 | rz[0][3] = 0 82 | 83 | rz[1][0] = float32(math.Sin(z)) 84 | rz[1][1] = float32(math.Cos(z)) 85 | rz[1][2] = 0 86 | rz[1][3] = 0 87 | 88 | rz[2][0] = 0 89 | rz[2][1] = 0 90 | rz[2][2] = 1 91 | rz[2][3] = 0 92 | 93 | rz[3][0] = 0 94 | rz[3][1] = 0 95 | rz[3][2] = 0 96 | rz[3][3] = 1 97 | 98 | rx[0][0] = 1 99 | rx[0][1] = 0 100 | rx[0][2] = 0 101 | rx[0][3] = 0 102 | 103 | rx[1][0] = 0 104 | rx[1][1] = float32(math.Cos(x)) 105 | rx[1][2] = -float32(math.Sin(x)) 106 | rx[1][3] = 0 107 | 108 | rx[2][0] = 0 109 | rx[2][1] = float32(math.Sin(x)) 110 | rx[2][2] = float32(math.Cos(x)) 111 | rx[2][3] = 0 112 | 113 | rx[3][0] = 0 114 | rx[3][1] = 0 115 | rx[3][2] = 0 116 | rx[3][3] = 1 117 | 118 | ry[0][0] = float32(math.Cos(y)) 119 | ry[0][1] = 0 120 | ry[0][2] = -float32(math.Sin(y)) 121 | ry[0][3] = 0 122 | 123 | ry[1][0] = 0 124 | ry[1][1] = 1 125 | ry[1][2] = 0 126 | ry[1][3] = 0 127 | 128 | ry[2][0] = float32(math.Sin(y)) 129 | ry[2][1] = 0 130 | ry[2][2] = float32(math.Cos(y)) 131 | ry[2][3] = 0 132 | 133 | ry[3][0] = 0 134 | ry[3][1] = 0 135 | ry[3][2] = 0 136 | ry[3][3] = 1 137 | 138 | *m = rz.mul(ry.mul(rx)) 139 | } 140 | 141 | func (m *Matrix4f) initScale(x, y, z float32) { 142 | m[0][0] = x 143 | m[0][1] = 0 144 | m[0][2] = 0 145 | m[0][3] = 0 146 | 147 | m[1][0] = 0 148 | m[1][1] = y 149 | m[1][2] = 0 150 | m[1][3] = 0 151 | 152 | m[2][0] = 0 153 | m[2][1] = 0 154 | m[2][2] = z 155 | m[2][3] = 0 156 | 157 | m[3][0] = 0 158 | m[3][1] = 0 159 | m[3][2] = 0 160 | m[3][3] = 1 161 | 162 | } 163 | 164 | func (m *Matrix4f) initProjection(fov, width, height, zNear, zFar float32) { 165 | ar := width / height 166 | 167 | tanHalfFOV := float32(math.Tan(float64(toRadians(fov / 2)))) 168 | zRange := zNear - zFar 169 | 170 | m[0][0] = 1.0 / (tanHalfFOV * ar) 171 | m[0][1] = 0 172 | m[0][2] = 0 173 | m[0][3] = 0 174 | m[1][0] = 0 175 | m[1][1] = 1 / tanHalfFOV 176 | m[1][2] = 0 177 | m[1][3] = 0 178 | m[2][0] = 0 179 | m[2][1] = 0 180 | m[2][2] = (-zNear - zFar) / zRange 181 | m[2][3] = 2 * zFar * zNear / zRange 182 | m[3][0] = 0 183 | m[3][1] = 0 184 | m[3][2] = 1 185 | m[3][3] = 0 186 | } 187 | 188 | func (m *Matrix4f) initCamera(forward, up Vector3f) { 189 | f, r := forward.normalised(), up.normalised() 190 | r = r.cross(f) 191 | u := f.cross(r) 192 | 193 | m[0][0] = r.X 194 | m[0][1] = r.Y 195 | m[0][2] = r.Z 196 | m[0][3] = 0 197 | 198 | m[1][0] = u.X 199 | m[1][1] = u.Y 200 | m[1][2] = u.Z 201 | m[1][3] = 0 202 | 203 | m[2][0] = f.X 204 | m[2][1] = f.Y 205 | m[2][2] = f.Z 206 | m[2][3] = 0 207 | 208 | m[3][0] = 0 209 | m[3][1] = 0 210 | m[3][2] = 0 211 | m[3][3] = 1 212 | } 213 | 214 | func (m *Matrix4f) mul(r Matrix4f) (res Matrix4f) { 215 | for i := 0; i < len(m); i++ { 216 | for j := 0; j < len(m[0]); j++ { 217 | res[i][j] = m[i][0]*r[0][j] + 218 | m[i][1]*r[1][j] + 219 | m[i][2]*r[2][j] + 220 | m[i][3]*r[3][j] 221 | } 222 | } 223 | return 224 | } 225 | 226 | func (m *Matrix4f) asArray() (result [16]float32) { 227 | index := 0 228 | for i := 0; i < len(m); i++ { 229 | for j := 0; j < len(m[0]); j++ { 230 | result[index] = m[i][j] 231 | index++ 232 | } 233 | } 234 | return 235 | } 236 | 237 | func (m *Matrix4f) String() string { 238 | r := "[" 239 | for i := 0; i < len(m); i++ { 240 | for j := 0; j < len(m[0]); j++ { 241 | r += fmt.Sprintf("%.3f, ", m[i][j]) 242 | } 243 | r = r[:len(r)-2] + " | " 244 | } 245 | r = r[:len(r)-2] + "]" 246 | 247 | return r 248 | } 249 | -------------------------------------------------------------------------------- /src/door.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import "time" 22 | 23 | const ( 24 | doorLength = 1 25 | doorHeight = 1 26 | doorWidth = 0.125 27 | doorStart = 0 28 | timeToOpen = time.Duration(300) * time.Millisecond 29 | closeDelay = time.Duration(3) * time.Second 30 | ) 31 | 32 | var _defaultDoorMesh Mesh 33 | 34 | type Door struct { 35 | mesh Mesh 36 | material *Material 37 | transform *Transform 38 | openPosition, closePosition Vector3f 39 | isOpening bool 40 | openingStartTime, openTime, closingStartTime, closeTime time.Time 41 | 42 | game *Game 43 | } 44 | 45 | func getDoorMesh() Mesh { 46 | if _defaultDoorMesh.IsEmpty() { 47 | vertices := []*Vertex{ 48 | &Vertex{Vector3f{doorStart, doorStart, doorStart}, Vector2f{0.5, 1}, Vector3f{}}, 49 | &Vertex{Vector3f{doorStart, doorHeight, doorStart}, Vector2f{0.5, 0.75}, Vector3f{}}, 50 | &Vertex{Vector3f{doorLength, doorHeight, doorStart}, Vector2f{0.75, 0.75}, Vector3f{}}, 51 | &Vertex{Vector3f{doorLength, doorStart, doorStart}, Vector2f{0.75, 1}, Vector3f{}}, 52 | 53 | &Vertex{Vector3f{doorStart, doorStart, doorStart}, Vector2f{0.73, 1}, Vector3f{}}, 54 | &Vertex{Vector3f{doorStart, doorHeight, doorStart}, Vector2f{0.73, 0.75}, Vector3f{}}, 55 | &Vertex{Vector3f{doorStart, doorHeight, doorWidth}, Vector2f{0.75, 0.75}, Vector3f{}}, 56 | &Vertex{Vector3f{doorStart, doorStart, doorWidth}, Vector2f{0.75, 1}, Vector3f{}}, 57 | 58 | &Vertex{Vector3f{doorStart, doorStart, doorWidth}, Vector2f{0.5, 1}, Vector3f{}}, 59 | &Vertex{Vector3f{doorStart, doorHeight, doorWidth}, Vector2f{0.5, 0.75}, Vector3f{}}, 60 | &Vertex{Vector3f{doorLength, doorHeight, doorWidth}, Vector2f{0.75, 0.75}, Vector3f{}}, 61 | &Vertex{Vector3f{doorLength, doorStart, doorWidth}, Vector2f{0.75, 1}, Vector3f{}}, 62 | 63 | &Vertex{Vector3f{doorLength, doorStart, doorStart}, Vector2f{0.73, 1}, Vector3f{}}, 64 | &Vertex{Vector3f{doorLength, doorHeight, doorStart}, Vector2f{0.73, 0.75}, Vector3f{}}, 65 | &Vertex{Vector3f{doorLength, doorHeight, doorWidth}, Vector2f{0.75, 0.75}, Vector3f{}}, 66 | &Vertex{Vector3f{doorLength, doorStart, doorWidth}, Vector2f{0.75, 1}, Vector3f{}}, 67 | } 68 | 69 | indices := []int32{ 70 | 0, 1, 2, 71 | 0, 2, 3, 72 | 6, 5, 4, 73 | 7, 6, 4, 74 | 10, 9, 8, 75 | 11, 10, 8, 76 | 12, 13, 14, 77 | 12, 14, 15} 78 | 79 | _defaultDoorMesh = NewMesh(vertices, indices, false) 80 | } 81 | 82 | return _defaultDoorMesh 83 | } 84 | 85 | func (g *Game) NewDoor(transform *Transform, material *Material, openPosition Vector3f) *Door { 86 | d := Door{} 87 | d.game = g 88 | 89 | d.mesh = getDoorMesh() 90 | 91 | d.transform, d.material, d.openPosition = transform, material, openPosition 92 | d.closePosition = d.transform.translation.mulf(1) 93 | 94 | return &d 95 | } 96 | 97 | func (d *Door) open() { 98 | if d.isOpening { 99 | return 100 | } 101 | 102 | d.openingStartTime = time.Now() 103 | d.openTime = d.openingStartTime.Add(timeToOpen) 104 | d.closingStartTime = d.openTime.Add(closeDelay) 105 | d.closeTime = d.closingStartTime.Add(timeToOpen) 106 | 107 | d.isOpening = true 108 | } 109 | 110 | func getIncrements(now, target time.Time, delta time.Duration) float32 { 111 | t := float32(now.Sub(target).Nanoseconds()) 112 | 113 | return t / float32(delta.Nanoseconds()) 114 | } 115 | 116 | func vectorLerp(startPos, endPos Vector3f, lerpFactor float32) Vector3f { 117 | return startPos.add(endPos.sub(startPos).mulf(lerpFactor)) 118 | } 119 | 120 | func (d *Door) update() { 121 | if d.isOpening { 122 | now := time.Now() 123 | 124 | if now.Before(d.openTime) { 125 | d.transform.translation = vectorLerp(d.closePosition, d.openPosition, getIncrements(now, d.openingStartTime, timeToOpen)) 126 | } else if now.Before(d.closingStartTime) { 127 | d.transform.translation = d.openPosition 128 | } else if now.Before(d.closeTime) { 129 | d.transform.translation = vectorLerp(d.openPosition, d.closePosition, getIncrements(now, d.closingStartTime, timeToOpen)) 130 | } else { 131 | d.transform.translation = d.closePosition 132 | d.isOpening = false 133 | } 134 | } 135 | } 136 | 137 | func (d *Door) render() { 138 | t := d.transform.getProjectedTransformation(d.game.Camera()) 139 | d.game.level.shader.updateUniforms(t, d.material) 140 | d.mesh.draw() 141 | } 142 | 143 | func (d *Door) getSize() Vector2f { 144 | if d.transform.rotation.Y == 90 { 145 | return Vector2f{doorWidth, doorLength} 146 | } 147 | 148 | return Vector2f{doorLength, doorWidth} 149 | } 150 | -------------------------------------------------------------------------------- /src/map.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | _ "image/png" 24 | "os" 25 | ) 26 | 27 | type Special byte 28 | 29 | const ( 30 | UnknownSpecial Special = 0 31 | PlayerA Special = 'A' 32 | PlayerB Special = 'B' 33 | Pistol Special = 'P' 34 | Gun Special = 'G' 35 | Rocket Special = 'R' 36 | Plasma Special = 'S' 37 | Chaingun Special = 'C' 38 | PistolAmmo Special = 'I' 39 | GunAmmo Special = 'U' 40 | RocketAmmo Special = 'O' 41 | PlasmaAmmo Special = 'L' 42 | BigMedkit Special = 'M' 43 | SmallMedkit Special = 'm' 44 | LightAmplificatorVisor Special = 'V' 45 | DoorSpecial Special = 'd' 46 | MonsterSpecial Special = 'e' 47 | ExitSpecial Special = 'X' 48 | Empty Special = ' ' 49 | ) 50 | 51 | type wallDef [4]float32 52 | 53 | func (wd *wallDef) String() string { 54 | return fmt.Sprintf("{%.2f, %.2f, %.2f, %.2f}", wd[0], wd[1], wd[2], wd[3]) 55 | } 56 | 57 | type Map struct { 58 | wallDefs []wallDef 59 | walls, planes, specials [][]byte 60 | width, height int 61 | } 62 | 63 | type mapError struct { 64 | fileName string 65 | err error 66 | } 67 | 68 | func (be mapError) Error() string { 69 | return fmt.Sprintf("NewMap(%s): %v", be.fileName, be.err) 70 | } 71 | 72 | func NewMap(fileName string) (*Map, error) { 73 | b := Map{} 74 | 75 | err := b.loadMap(fileName) 76 | if err != nil { 77 | return nil, mapError{fileName, err} 78 | } 79 | 80 | return &b, nil 81 | } 82 | 83 | func (m *Map) loadMap(fileName string) error { 84 | f, err := os.Open("./maps/" + fileName) 85 | if err != nil { 86 | return err 87 | } 88 | defer f.Close() 89 | 90 | my := map[int]wallDef{} 91 | var maxWallIndex int 92 | lineNum := 1 93 | for { 94 | var wallIndex int 95 | var coords wallDef 96 | read, _ := fmt.Fscanf(f, "wall%d {%f,%f,%f,%f}\n", &wallIndex, &coords[0], &coords[1], &coords[2], &coords[3]) 97 | 98 | if read == 0 { 99 | // finished wall declarations 100 | break 101 | } else if read != 5 { 102 | return fmt.Errorf("invalid wall row at line %d (read %d fields)", lineNum, read) 103 | } 104 | 105 | // add wall definition 106 | my[wallIndex] = coords 107 | 108 | if wallIndex > maxWallIndex { 109 | maxWallIndex = wallIndex 110 | } 111 | 112 | lineNum++ 113 | } 114 | 115 | // append second natural order 116 | for i := 1; i <= maxWallIndex; i++ { 117 | m.wallDefs = append(m.wallDefs, my[i]) 118 | } 119 | 120 | // go back 1 byte, as eaten by Fscanf() to peek-read 121 | _, err = f.Seek(-1, 1) 122 | if err != nil { 123 | return err 124 | } 125 | 126 | // read map size 127 | var sz uint 128 | read, _ := fmt.Fscanf(f, "lengthmap %3d\nMAP:\n", &sz) 129 | if read != 1 { 130 | return fmt.Errorf("no valid lengthmap declaration at line %d", lineNum) 131 | } 132 | lineNum++ 133 | lineNum++ 134 | 135 | m.width, m.height = int(sz), int(sz) 136 | 137 | // now read all map data 138 | m.walls, err = readMapBlock(f, m.width, m.height, &lineNum) 139 | if err != nil { 140 | return err 141 | } 142 | 143 | _, err = fmt.Fscanf(f, "PLANES:\n") 144 | if err != nil { 145 | return fmt.Errorf("could not match PLANES declaration at line %d (%v)", lineNum, err) 146 | } 147 | m.planes, err = readMapBlock(f, m.width, m.height, &lineNum) 148 | if err != nil { 149 | return err 150 | } 151 | 152 | _, err = fmt.Fscanf(f, "SPECIALS:\n") 153 | if err != nil { 154 | return fmt.Errorf("could not match SPECIALS declaration at line %d (%v)", lineNum, err) 155 | } 156 | m.specials, err = readMapBlock(f, m.width, m.height, &lineNum) 157 | if err != nil { 158 | return err 159 | } 160 | 161 | return nil 162 | } 163 | 164 | func readMapBlock(f *os.File, w, h int, lineNum *int) ([][]byte, error) { 165 | block := make([][]byte, w) 166 | for row := 0; row < h; row++ { 167 | block[row] = make([]byte, w) 168 | n, err := f.Read(block[row]) 169 | if err != nil { 170 | return block, err 171 | } 172 | if n != w { 173 | return block, fmt.Errorf("not enough data at line %d", *lineNum) 174 | } 175 | 176 | // skip newline 177 | var nl [1]byte 178 | _, err = f.Read(nl[:]) 179 | if err != nil { 180 | return block, err 181 | } 182 | if nl[0] != 0xA { 183 | return block, fmt.Errorf("invalid line termination at line %d (%d)", *lineNum, nl[0]) 184 | } 185 | (*lineNum)++ 186 | } 187 | return block, nil 188 | } 189 | 190 | func (m *Map) IsEmpty(x, y int) bool { 191 | return m.specials[x][y] == ' ' && m.walls[x][y] == ' ' && m.planes[x][y] == ' ' 192 | } 193 | 194 | func (m *Map) WallTexCoords(x, y int) wallDef { 195 | wdIndex := m.walls[x][y] - 48 - 1 196 | return m.wallDefs[wdIndex] 197 | } 198 | 199 | func (m *Map) PlaneTexCoords(x, y int) wallDef { 200 | wdIndex := m.planes[x][y] - 48 - 1 201 | return m.wallDefs[wdIndex] 202 | } 203 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "math/rand" 24 | "os" 25 | "runtime" 26 | "time" 27 | "unsafe" 28 | 29 | "github.com/gdm85/wolfengo/src/gl" 30 | "github.com/go-gl/glfw/v3.1/glfw" 31 | ) 32 | 33 | const ( 34 | version = "0.1.4" 35 | debugGL = true // extended debugging of GL calls 36 | printFPS = true // print FPS count every second 37 | debugLevelTest = false // will load 'levelTest.map' 38 | frameCap = float64(250) // cap max framerate to this number of FPS 39 | ) 40 | 41 | var ( 42 | Window *glfw.Window 43 | G *Game 44 | random *rand.Rand 45 | ) 46 | 47 | func init() { 48 | // This is needed to arrange that main() runs on main thread. 49 | runtime.LockOSThread() 50 | 51 | random = rand.New(rand.NewSource(time.Now().Unix())) 52 | } 53 | 54 | func fatalError(err error) { 55 | fmt.Fprintf(os.Stderr, "FATAL: %v\n", err) 56 | os.Exit(1) 57 | } 58 | 59 | func debugCb( 60 | source uint32, 61 | gltype uint32, 62 | id uint32, 63 | severity uint32, 64 | length int32, 65 | message string, 66 | userParam unsafe.Pointer) { 67 | 68 | msg := fmt.Sprintf("[GL_DEBUG] source %d gltype %d id %d severity %d length %d: %s", source, gltype, id, severity, length, message) 69 | if severity == gl.DEBUG_SEVERITY_HIGH { 70 | panic(msg) 71 | } 72 | fmt.Fprintln(os.Stderr, msg) 73 | } 74 | 75 | // needed for some older hardware e.g. Intel HD graphics 76 | // notice that this suffix only changes a couple shaders, while the rest uses the 330 version 77 | var shaderVersion = "120" 78 | 79 | func main() { 80 | fmt.Printf(`WolfenGo v%s, Copyright (C) 2016~2019 gdm85 81 | https://github.com/gdm85/wolfengo 82 | WolfenGo comes with ABSOLUTELY NO WARRANTY. 83 | This is free software, and you are welcome to redistribute it 84 | under GNU/GPLv2 license.`+"\n", version) 85 | 86 | if err := glfw.Init(); err != nil { 87 | fatalError(err) 88 | } 89 | defer glfw.Terminate() 90 | 91 | // create a context and activate it 92 | glfw.WindowHint(glfw.ContextVersionMajor, gl.RequestedContextVersionMajor) 93 | glfw.WindowHint(glfw.ContextVersionMinor, gl.RequestedContextVersionMinor) 94 | if gl.RequestedContextVersionMajor >= 3 { 95 | // switch to version 330 96 | shaderVersion = "" 97 | glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) 98 | if gl.RequestedContextVersionMajor >= 2 { 99 | glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) 100 | } 101 | } 102 | glfw.WindowHint(glfw.Resizable, glfw.False) 103 | glfw.WindowHint(glfw.DoubleBuffer, glfw.True) 104 | var err error 105 | Window, err = glfw.CreateWindow(800, 600, "WolfenGo", nil, nil) 106 | if err != nil { 107 | fatalError(err) 108 | } 109 | Window.MakeContextCurrent() 110 | 111 | // gl.Init() should be called after context is current 112 | if err := gl.Init(); err != nil { 113 | fatalError(err) 114 | } 115 | 116 | glfw.SwapInterval(1) // enable vertical refresh 117 | 118 | fmt.Println(gl.GoStr(gl.GetString(gl.VERSION))) 119 | 120 | // temporary workaround until https://github.com/go-gl/gl/issues/40 is addressed 121 | if debugGL && runtime.GOOS != "darwin" { 122 | gl.DebugMessageCallback(debugCb, unsafe.Pointer(nil)) 123 | gl.Enable(gl.DEBUG_OUTPUT) 124 | } 125 | 126 | // init graphics 127 | gl.ClearColor(0.0, 0.0, 0.0, 0.0) 128 | 129 | gl.FrontFace(gl.CW) 130 | gl.CullFace(gl.BACK) 131 | gl.Enable(gl.CULL_FACE) 132 | gl.Enable(gl.DEPTH_TEST) 133 | 134 | gl.Enable(gl.BLEND) 135 | gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) 136 | 137 | gl.Enable(gl.DEPTH_CLAMP) 138 | gl.Enable(gl.TEXTURE_2D) 139 | 140 | // load all assets 141 | _, err = getBasicShader() 142 | if err != nil { 143 | fatalError(err) 144 | } 145 | err = _defaultMedkit.initMedkit() 146 | if err != nil { 147 | fatalError(err) 148 | } 149 | err = _defaultMonster.initMonster() 150 | if err != nil { 151 | fatalError(err) 152 | } 153 | getDoorMesh() 154 | initPlayer() 155 | err = initGun() 156 | if err != nil { 157 | fatalError(err) 158 | } 159 | 160 | G, err = NewGame() 161 | if err != nil { 162 | fatalError(err) 163 | } 164 | 165 | var frames uint64 166 | var frameCounter time.Duration 167 | 168 | frameTime := time.Duration(1000/frameCap) * time.Millisecond 169 | lastTime := time.Now() 170 | var unprocessedTime time.Duration 171 | 172 | // GAME STARTS 173 | for { 174 | var render bool 175 | 176 | startTime := time.Now() 177 | passedTime := startTime.Sub(lastTime) 178 | lastTime = startTime 179 | 180 | unprocessedTime += passedTime 181 | frameCounter += passedTime 182 | 183 | for unprocessedTime > frameTime { 184 | render = true 185 | 186 | unprocessedTime -= frameTime 187 | if Window.ShouldClose() { 188 | goto Exit 189 | } 190 | G.timeDelta = frameTime.Seconds() 191 | 192 | glfw.PollEvents() 193 | err := G.input() 194 | if err != nil { 195 | fatalError(err) 196 | } 197 | err = G.update() 198 | if err != nil { 199 | fatalError(err) 200 | } 201 | 202 | if frameCounter >= time.Second { 203 | if printFPS { 204 | fmt.Printf("%d FPS\n", frames) 205 | } 206 | frames = 0 207 | frameCounter -= time.Second 208 | } 209 | } 210 | 211 | if render { 212 | gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) 213 | 214 | G.render() 215 | frames++ 216 | Window.SwapBuffers() 217 | } else { 218 | time.Sleep(time.Millisecond) 219 | } 220 | } 221 | 222 | Exit: 223 | Window.Destroy() 224 | } 225 | -------------------------------------------------------------------------------- /src/player.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "math/rand" 24 | 25 | "github.com/go-gl/glfw/v3.1/glfw" 26 | ) 27 | 28 | const ( 29 | gunOffset = -0.0875 30 | playerMouseSensitivity = 0.2 31 | ) 32 | 33 | type Player struct { 34 | mesh Mesh 35 | gunMaterial *Material 36 | 37 | gunTransform *Transform 38 | camera *Camera 39 | health int 40 | movementVector Vector3f 41 | 42 | game *Game 43 | } 44 | 45 | var ( 46 | defaultPlayer = Object{ 47 | K: 1.0379746835443037974683544303797, 48 | scale: 0.0625, 49 | damageMin: 20, 50 | damageMax: 60, 51 | maxHealth: 100, 52 | // player is slightly faster than monsters 53 | moveSpeed: 2.5, 54 | 55 | size: 0.2, 56 | shootDistance: 1000.0, 57 | } 58 | 59 | defaultGunMaterial *Material 60 | ) 61 | 62 | func init() { 63 | defaultPlayer.sizeY = defaultPlayer.scale 64 | defaultPlayer.sizeX = defaultPlayer.sizeY / (defaultPlayer.K * 2) 65 | defaultPlayer.texMinX = -defaultPlayer.offsetX 66 | defaultPlayer.texMaxX = -1 - defaultPlayer.offsetX 67 | defaultPlayer.texMinY = -defaultPlayer.offsetY 68 | defaultPlayer.texMaxY = 1 - defaultPlayer.offsetY 69 | } 70 | 71 | func initPlayer() { 72 | vertices := []*Vertex{ 73 | &Vertex{Vector3f{-defaultPlayer.sizeX, defaultPlayer.start, defaultPlayer.start}, Vector2f{defaultPlayer.texMaxX, defaultPlayer.texMaxY}, Vector3f{}}, 74 | &Vertex{Vector3f{-defaultPlayer.sizeX, defaultPlayer.sizeY, defaultPlayer.start}, Vector2f{defaultPlayer.texMaxX, defaultPlayer.texMinY}, Vector3f{}}, 75 | &Vertex{Vector3f{defaultPlayer.sizeX, defaultPlayer.sizeY, defaultPlayer.start}, Vector2f{defaultPlayer.texMinX, defaultPlayer.texMinY}, Vector3f{}}, 76 | &Vertex{Vector3f{defaultPlayer.sizeX, defaultPlayer.start, defaultPlayer.start}, Vector2f{defaultPlayer.texMinX, defaultPlayer.texMaxY}, Vector3f{}}, 77 | } 78 | 79 | indices := []int32{0, 1, 2, 0, 2, 3} 80 | 81 | defaultPlayer.mesh = NewMesh(vertices, indices, false) 82 | } 83 | 84 | func initGun() error { 85 | t, err := NewTexture("PISGB0.png") 86 | if err != nil { 87 | return err 88 | } 89 | defaultGunMaterial = NewMaterial(t) 90 | return nil 91 | } 92 | 93 | func (g *Game) NewPlayer(position Vector3f, playerMesh Mesh, gunMaterial *Material) *Player { 94 | p := Player{} 95 | p.game = g 96 | p.mesh = playerMesh 97 | p.gunMaterial = gunMaterial 98 | p.camera = NewCamera(position, Vector3f{0, 0, -1}, Vector3f{0, 1, 0}, playerMouseSensitivity) 99 | p.health = defaultPlayer.maxHealth 100 | p.gunTransform = g.NewTransform() 101 | p.gunTransform.translation = Vector3f{7, 0, 7} 102 | 103 | return &p 104 | } 105 | 106 | func (p *Player) damage(amt int) { 107 | p.health -= amt 108 | 109 | // as this function is used to give health too, check for maximum overflow 110 | if p.health > defaultPlayer.maxHealth { 111 | p.health = defaultPlayer.maxHealth 112 | } else if p.health <= 0 { 113 | p.game.isRunning = false 114 | fmt.Println("You just died! GAME OVER") 115 | } 116 | // this println was in original clone 117 | fmt.Println("player health =", p.health) 118 | } 119 | 120 | func getPlayerDamage() int { 121 | return rand.Intn(defaultPlayer.damageMax-defaultPlayer.damageMin) + defaultPlayer.damageMin 122 | } 123 | 124 | func (p *Player) update() { 125 | movAmt := defaultPlayer.moveSpeed * float32(p.game.timeDelta) 126 | 127 | p.movementVector.Y = 0 128 | if p.movementVector.length() > 0 { 129 | p.movementVector = p.movementVector.normalised() 130 | } 131 | 132 | oldPos := p.camera.pos 133 | newPos := oldPos.add(p.movementVector.mulf(movAmt)) 134 | 135 | collisionVector := p.game.level.checkCollision(oldPos, newPos, defaultPlayer.size, defaultPlayer.size) 136 | p.movementVector = p.movementVector.mul(collisionVector) 137 | 138 | if p.movementVector.length() > 0 { 139 | p.camera.move(p.movementVector, movAmt) 140 | } 141 | 142 | p.gunTransform.translation = p.camera.pos.add(p.camera.forward.normalised().mulf(0.105)) 143 | p.gunTransform.translation.Y += gunOffset 144 | 145 | directionToCamera := p.camera.pos.sub(p.gunTransform.translation) 146 | 147 | angleToFaceTheCamera := AtanAndToDegrees(directionToCamera.Z / directionToCamera.X) 148 | if directionToCamera.X < 0 { 149 | angleToFaceTheCamera += 180 150 | } 151 | p.gunTransform.rotation.Y = angleToFaceTheCamera + 90 152 | } 153 | 154 | func (p *Player) input() error { 155 | if Window.GetKey(glfw.KeyE) == glfw.Press { 156 | err := p.game.level.openDoors(p.camera.pos, true) 157 | if err != nil { 158 | return err 159 | } 160 | } 161 | 162 | if Window.GetKey(glfw.KeyEscape) == glfw.Press { 163 | Window.SetInputMode(glfw.CursorMode, glfw.CursorNormal) 164 | p.game.UnlockMouse() 165 | } 166 | 167 | // wait for left mouse click to lock the camera to the mouse 168 | if Window.GetMouseButton(glfw.MouseButtonLeft) == glfw.Press { 169 | if !p.game.mouseLocked { 170 | Window.SetInputMode(glfw.CursorMode, glfw.CursorDisabled) 171 | p.game.LockMouse() 172 | } else { 173 | // shoot a bullet 174 | lineStart := Vector2f{p.camera.pos.X, p.camera.pos.Z} 175 | castDirection := Vector2f{p.camera.forward.X, p.camera.forward.Z}.normalised() 176 | lineEnd := lineStart.add(castDirection.mulf(defaultPlayer.shootDistance)) 177 | 178 | p.game.level.checkIntersections(lineStart, lineEnd, true) 179 | } 180 | } 181 | 182 | p.movementVector = Vector3f{0, 0, 0} 183 | 184 | if Window.GetKey(glfw.KeyW) == glfw.Press { 185 | p.movementVector = p.movementVector.add(p.camera.forward) 186 | } 187 | if Window.GetKey(glfw.KeyS) == glfw.Press { 188 | p.movementVector = p.movementVector.sub(p.camera.forward) 189 | } 190 | if Window.GetKey(glfw.KeyA) == glfw.Press { 191 | p.movementVector = p.movementVector.add(p.camera.getLeft()) 192 | } 193 | if Window.GetKey(glfw.KeyD) == glfw.Press { 194 | p.movementVector = p.movementVector.add(p.camera.getRight()) 195 | } 196 | if Window.GetKey(glfw.KeyQ) == glfw.Press { 197 | Window.SetShouldClose(true) 198 | } 199 | if p.game.mouseLocked { 200 | p.camera.mouseLook(&p.game.oldPosition) 201 | } 202 | 203 | return nil 204 | } 205 | 206 | func (p *Player) render() { 207 | p.game.level.shader.updateUniforms(p.gunTransform.getProjectedTransformation(p.camera), p.gunMaterial) 208 | p.mesh.draw() 209 | } 210 | -------------------------------------------------------------------------------- /src/monster.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "time" 23 | ) 24 | 25 | const ( 26 | stateIdle = iota 27 | stateChase 28 | stateAttack 29 | stateDying 30 | stateDead 31 | ) 32 | 33 | const ( 34 | offsetFromGround = 0.0 // -0.075 35 | movementStopDistance = 1.5 36 | shootAngle = 10.0 37 | attackChance = 0.5 38 | ) 39 | 40 | var ( 41 | monsterAnimationFrames = []string{ 42 | "SSWVA1.png", 43 | "SSWVB1.png", 44 | "SSWVC1.png", 45 | "SSWVD1.png", 46 | 47 | "SSWVE0.png", 48 | "SSWVF0.png", 49 | "SSWVG0.png", 50 | 51 | "SSWVH0.png", 52 | 53 | "SSWVI0.png", 54 | "SSWVJ0.png", 55 | "SSWVK0.png", 56 | "SSWVL0.png", 57 | "SSWVM0.png", 58 | } 59 | _defaultMonster = Object{K: 1.9310344827586206896551724137931, 60 | scale: 0.7, 61 | 62 | offsetX: 0.0, // 0.05 63 | offsetY: 0.0, // 0.01 64 | 65 | moveSpeed: 2.0, 66 | 67 | size: 0.2, 68 | 69 | shootDistance: 1000.0, 70 | 71 | maxHealth: 100, 72 | damageMin: 5, 73 | damageMax: 30, 74 | } 75 | defaultMonsterSize = Vector2f{_defaultMonster.size, _defaultMonster.size} 76 | ) 77 | 78 | func init() { 79 | _defaultMonster.sizeY = _defaultMonster.scale 80 | _defaultMonster.sizeX = _defaultMonster.sizeY / (_defaultMonster.K * 2) 81 | _defaultMonster.texMinX = -_defaultMonster.offsetX 82 | _defaultMonster.texMaxX = -1 - _defaultMonster.offsetX 83 | _defaultMonster.texMinY = -_defaultMonster.offsetY 84 | _defaultMonster.texMaxY = 1 - _defaultMonster.offsetY 85 | } 86 | 87 | func (m *Object) initMonster() error { 88 | m.animations = make([]*Texture, len(monsterAnimationFrames)) 89 | for i := 0; i < len(monsterAnimationFrames); i++ { 90 | var err error 91 | m.animations[i], err = NewTexture(monsterAnimationFrames[i]) 92 | if err != nil { 93 | return err 94 | } 95 | } 96 | 97 | vertices := []*Vertex{ 98 | &Vertex{Vector3f{-m.sizeX, m.start, m.start}, Vector2f{m.texMaxX, m.texMaxY}, Vector3f{0, 0, 0}}, 99 | &Vertex{Vector3f{-m.sizeX, m.sizeY, m.start}, Vector2f{m.texMaxX, m.texMinY}, Vector3f{0, 0, 0}}, 100 | &Vertex{Vector3f{m.sizeX, m.sizeY, m.start}, Vector2f{m.texMinX, m.texMinY}, Vector3f{0, 0, 0}}, 101 | &Vertex{Vector3f{m.sizeX, m.start, m.start}, Vector2f{m.texMinX, m.texMaxY}, Vector3f{0, 0, 0}}, 102 | } 103 | 104 | indices := []int32{0, 1, 2, 0, 2, 3} 105 | 106 | m.mesh = NewMesh(vertices, indices, false) 107 | return nil 108 | } 109 | 110 | type Monster struct { 111 | transform *Transform 112 | state int 113 | canAttack bool 114 | canLook bool 115 | health int 116 | material *Material 117 | deathTime time.Time 118 | animations []*Texture 119 | mesh Mesh 120 | 121 | game *Game 122 | } 123 | 124 | func (g *Game) NewMonster(t *Transform, animations []*Texture) *Monster { 125 | m := Monster{} 126 | 127 | m.mesh = _defaultMonster.mesh 128 | m.transform = t 129 | m.game = g 130 | m.state = stateIdle 131 | m.health = _defaultMonster.maxHealth 132 | m.animations = animations 133 | m.material = NewMaterial(m.animations[0]) 134 | 135 | return &m 136 | } 137 | 138 | func (m *Monster) damage(amt int) { 139 | if m.state == stateIdle { 140 | m.state = stateChase 141 | } 142 | 143 | m.health -= amt 144 | 145 | if m.health <= 0 { 146 | m.state = stateDying 147 | } 148 | } 149 | 150 | func getDecimals() float32 { 151 | now := time.Now() 152 | ns := float32(now.UnixNano() - now.Unix()*1e9) 153 | 154 | return ns / float32(1e9) 155 | } 156 | 157 | func (m *Monster) idleUpdate(orientation Vector3f, distance float32) { 158 | if getDecimals() < 0.5 { 159 | m.canLook = true 160 | m.material.texture = m.animations[0] 161 | } else { 162 | if m.canLook { 163 | lineStart := Vector2f{m.transform.translation.X, m.transform.translation.Z} 164 | castDirection := Vector2f{orientation.X, orientation.Z} 165 | lineEnd := lineStart.add(castDirection.mulf(_defaultMonster.shootDistance)) 166 | 167 | collisionVector := m.game.level.checkIntersections(lineStart, lineEnd, false) 168 | playerIntersectVector := Vector2f{m.game.Camera().pos.X, m.game.Camera().pos.Z} 169 | 170 | if collisionVector == nil || playerIntersectVector.sub(lineStart).length() < collisionVector.sub(lineStart).length() { 171 | m.state = stateChase 172 | } 173 | 174 | m.canLook = false 175 | } 176 | } 177 | } 178 | 179 | func (m *Monster) chaseUpdate(orientation Vector3f, distance float32) error { 180 | timeDecimals := getDecimals() 181 | 182 | var animFrame int 183 | if timeDecimals < 0.25 { 184 | animFrame = 0 185 | } else if timeDecimals < 0.5 { 186 | animFrame = 1 187 | } else if timeDecimals < 0.75 { 188 | animFrame = 2 189 | } else { 190 | animFrame = 3 191 | } 192 | m.material.texture = m.animations[animFrame] 193 | 194 | if random.Float32() < attackChance*float32(m.game.timeDelta) { 195 | m.state = stateAttack 196 | } 197 | 198 | if distance > movementStopDistance { 199 | moveAmount := _defaultMonster.moveSpeed * float32(m.game.timeDelta) 200 | 201 | oldPos := m.transform.translation 202 | newPos := m.transform.translation.add(orientation.mulf(moveAmount)) 203 | 204 | collisionVector := m.game.level.checkCollision(oldPos, newPos, _defaultMonster.size, _defaultMonster.size) 205 | movementVector := collisionVector.mul(orientation) 206 | 207 | if movementVector.length() > 0 { 208 | m.transform.translation = m.transform.translation.add(movementVector.mulf(moveAmount)) 209 | } 210 | 211 | if movementVector.sub(orientation).length() != 0 { 212 | err := m.game.level.openDoors(m.transform.translation, false) 213 | if err != nil { 214 | return err 215 | } 216 | } 217 | } else { 218 | m.state = stateAttack 219 | } 220 | 221 | return nil 222 | } 223 | 224 | func (m *Monster) attackUpdate(orientation Vector3f, distance float32) { 225 | timeDecimals := getDecimals() 226 | 227 | if timeDecimals < 0.25 { 228 | m.material.texture = m.animations[4] 229 | } else if timeDecimals < 0.5 { 230 | m.material.texture = m.animations[5] 231 | } else if timeDecimals < 0.75 { 232 | m.material.texture = m.animations[6] 233 | if m.canAttack { 234 | lineStart := Vector2f{m.transform.translation.X, m.transform.translation.Z} 235 | castDirection := Vector2f{orientation.X, orientation.Z}.rotate((random.Float32() - 0.5) * shootAngle) 236 | lineEnd := lineStart.add(castDirection.mulf(_defaultMonster.shootDistance)) 237 | 238 | collisionVector := m.game.level.checkIntersections(lineStart, lineEnd, false) 239 | 240 | playerIntersectVector := lineIntersectRect(lineStart, lineEnd, Vector2f{m.game.Camera().pos.X, m.game.Camera().pos.Z}, Vector2f{defaultPlayer.size, defaultPlayer.size}) 241 | if playerIntersectVector != nil && (collisionVector == nil || playerIntersectVector.sub(lineStart).length() < collisionVector.sub(lineStart).length()) { 242 | m.game.level.damagePlayer(_defaultMonster.damageMin + random.Intn(_defaultMonster.damageMax-_defaultMonster.damageMin)) 243 | } 244 | 245 | m.canAttack = false 246 | } 247 | } else { 248 | m.material.texture = m.animations[5] 249 | m.state = stateChase 250 | m.canAttack = true 251 | } 252 | } 253 | 254 | const ( 255 | time1 = time.Duration(100) * time.Millisecond 256 | time2 = time.Duration(300) * time.Millisecond 257 | time3 = time.Duration(450) * time.Millisecond 258 | time4 = time.Duration(600) * time.Millisecond 259 | ) 260 | 261 | func (m *Monster) dyingUpdate(orientation Vector3f, distance float32) { 262 | now := time.Now() 263 | 264 | if m.deathTime.IsZero() { 265 | m.deathTime = now 266 | } 267 | 268 | if now.Before(m.deathTime.Add(time1)) { 269 | m.material.texture = m.animations[8] 270 | m.transform.scale = Vector3f{1, 0.96428571428571428571428571428571, 1} 271 | } else if now.Before(m.deathTime.Add(time2)) { 272 | m.material.texture = m.animations[9] 273 | m.transform.scale = Vector3f{1.7, 0.9, 1.0} 274 | } else if now.Before(m.deathTime.Add(time3)) { 275 | m.material.texture = m.animations[10] 276 | m.transform.scale = Vector3f{1.7, 0.9, 1.0} 277 | } else if now.Before(m.deathTime.Add(time4)) { 278 | m.material.texture = m.animations[11] 279 | m.transform.scale = Vector3f{1.7, 0.5, 1.0} 280 | } else { 281 | m.state = stateDead 282 | } 283 | } 284 | 285 | func (m *Monster) deadUpdate(orientation Vector3f, distance float32) { 286 | m.material.texture = m.animations[12] 287 | m.transform.scale = Vector3f{1.7586206896551724137931034482759, 0.28571428571428571428571428571429, 1} 288 | } 289 | 290 | func (m *Monster) alignWithGround() { 291 | m.transform.translation.Y = offsetFromGround 292 | } 293 | 294 | func (m *Monster) faceCamera(directionToCamera Vector3f) { 295 | angleToFaceTheCamera := AtanAndToDegrees(directionToCamera.Z / directionToCamera.X) 296 | 297 | if directionToCamera.X < 0 { 298 | angleToFaceTheCamera += 180 299 | } 300 | 301 | m.transform.rotation.Y = angleToFaceTheCamera + 90 302 | } 303 | 304 | func (m *Monster) update() error { 305 | directionToCamera := m.game.Camera().pos.sub(m.transform.translation) 306 | 307 | distance := directionToCamera.length() 308 | 309 | orientation := directionToCamera.divf(distance) 310 | 311 | m.alignWithGround() 312 | m.faceCamera(orientation) 313 | 314 | switch m.state { 315 | case stateIdle: 316 | m.idleUpdate(orientation, distance) 317 | case stateChase: 318 | err := m.chaseUpdate(orientation, distance) 319 | if err != nil { 320 | return err 321 | } 322 | case stateAttack: 323 | m.attackUpdate(orientation, distance) 324 | case stateDying: 325 | m.dyingUpdate(orientation, distance) 326 | case stateDead: 327 | m.deadUpdate(orientation, distance) 328 | default: 329 | panic("unexpected monster state") 330 | } 331 | 332 | return nil 333 | } 334 | 335 | func (m *Monster) render() { 336 | m.game.level.shader.updateUniforms(m.transform.getProjectedTransformation(m.game.Camera()), m.material) 337 | m.mesh.draw() 338 | } 339 | -------------------------------------------------------------------------------- /maps/level3.map: -------------------------------------------------------------------------------- 1 | wall3 {1.00,0.75,0.00,0.25} 2 | wall7 {0.75,0.50,0.50,0.75} 3 | wall1 {1.00,0.75,0.75,1.00} 4 | wall4 {0.75,0.50,0.00,0.25} 5 | wall5 {0.25,0.00,0.25,0.50} 6 | wall10 {0.50,0.25,0.25,0.50} 7 | wall12 {0.50,0.25,0.50,0.75} 8 | wall8 {1.00,0.75,0.50,0.75} 9 | wall9 {0.50,0.25,0.75,1.00} 10 | wall2 {0.25,0.00,0.00,0.25} 11 | wall6 {0.75,0.50,0.25,0.50} 12 | wall11 {1.00,0.75,0.25,0.50} 13 | lengthmap 064 14 | MAP: 15 | 16 | 222222222222222222 17 | 222222222222222222 18 | 22 2222222222 22 2232 2222 22222 19 | 24 4222222224 42 2222 2222 22222 222 222 20 | 222222222222222222 4222 422222222222222222 21 | 22222222222222222222222 2222 22222 222 222 22 | 222222222222222222 4222 2222 22222 23 | 24 4222222224 42 2222 2222 22322 5555555 24 | 22 2222222222 22 2222 2222 2 2 5 5 5 25 | 222222222222222222 2222 422222 225 6 5 26 | 222222222222222222 2222 2222 2 2 2 5 5 5 27 | 222222222222222222 2222 2222 22222 5555555 28 | 22 2222222222 22 2222 2 2 2 29 | 24 4222222224 42 2222 222 88888 222 30 | 222222222222222222 2222 424 8 9 8 222222 31 | 222222222222222222 22222223 8 8 8 2 222 32 | 222222222222222222 2222 424 8 9 8 22 228 33 | 24 4222222224 42 2222 222 88888 2 88 34 | 22 2222222222 22 8 8 22 35 | 222222222222222222 888888888888 2 36 | 222222222222222222 888888888888 2 37 | 222222222222222222 8 8 2 38 | 22 2222222222 22 88 32223 888898 5555555 39 | 24 4222222224 42 88 22222 88 8 5 5 5 40 | 222222222222222222 88 22222 8 898 5:555:5 41 | 222222222222222222888822222 8 888855:555:5 42 | 222222222222222222 88 22222 9 9888 5 5 5 43 | 24 4222222224 42 88 32223 888888 5555555 44 | 22 2222222222 22 88 8 ; 5 45 | 222222222222222222 88888888 ; 555 46 | 222222222222222222 88888888 ;;; :5: 47 | 8 ; 5555555 48 | 888 ;;; :55555: 49 | 888 ; 555555555 50 | 888 ;;; 655555555 51 | 888 ; 555555555 52 | 888 ;;; :55555: 53 | 888 ; 5555555 54 | 888 ;;; :5: 55 | 888 ; 555 56 | 888 ; 5 57 | 888 555255555555555 58 | 888 555555555555555 59 | 888 ;;;;;;;;;88 5:55:5555:55:55 60 | 888 ; 333 ; 88 55 55 55 5 61 | 888 ; 2<2 ; 88 55 55 55 5 62 | 888 ; ; 88 5:55:5555:55:55 63 | 888;;;;;;;;; 88 555555555555555 64 | 888;;;;;;;;; 88 5: :: :: :55 65 | 888 ;;;;;;; 88 555555555555555 66 | 88 8 8 67 | 8888888888888888888 68 | 8888888888888888888 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | PLANES: 80 | 81 | 111111111111111111 82 | 111111111111111111 83 | 11 1111111111 11 1111 1111 11111 84 | 11 1111111111 11 1111 1111 11111 111 222 85 | 111111111111111111 1111 111111111111112222 86 | 11111111111111111111111 1111 11111 111 222 87 | 111111111111111111 1111 1111 11111 88 | 11 1111111111 11 1111 1111 11111 1111111 89 | 11 1111111111 11 1111 1111 1 1 1 1 1 90 | 111111111111111111 1111 111111 111 1 1 91 | 111111111111111111 1111 1111 1 1 1 1 1 1 92 | 111111111111111111 1111 1111 11111 1111111 93 | 11 1111111111 11 1111 1 1 7 94 | 11 1111111111 11 1111 111 11111 777 95 | 111111111111111111 1111 111 1 1 1 227777 96 | 111111111111111111 11111111 1 1 1 2 777 97 | 111111111111111111 1111 111 1 1 1 22 777 98 | 11 1111111111 11 1111 111 11111 2 77 99 | 11 1111111111 11 7 7 22 100 | 111111111111111111 777777777777 2 101 | 111111111111111111 777777777777 2 102 | 111111111111111111 7 7 2 103 | 11 1111111111 11 77 11111 111111 1111111 104 | 11 1111111111 11 77 11111 11 1 1 1 1 105 | 111111111111111111 77 11111 1 111 1111111 106 | 111111111111111111777711111 1 111111111111 107 | 111111111111111111 77 11111 1 1111 1 1 1 108 | 11 1111111111 11 77 11111 111111 1111111 109 | 11 1111111111 11 77 7 7 1 110 | 111111111111111111 77777777 7 111 111 | 111111111111111111 77777777 777 111 112 | 7 7 1111111 113 | 777 777 1111111 114 | 777 7 111111111 115 | 777 777 111111111 116 | 777 7 111111111 117 | 777 777 1111111 118 | 777 7 1111111 119 | 777 777 111 120 | 777 7 111 121 | 777 7 1 122 | 777 111111111111111 123 | 777 111111111111111 124 | 777 ;;111;;;;77 111111111111111 125 | 777 ; 111 ; 77 11 11 11 1 126 | 777 ; 111 ; 77 11 11 11 1 127 | 777 ; ; 77 111111111111111 128 | 777;;;;;;;;; 77 111111111111111 129 | 777;;;;;;;;; 77 11 11 11 111 130 | 777 ;;;;;;; 77 111111111111111 131 | 77 7 7 132 | 7777777777777777777 133 | 7777777777777777777 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | SPECIALS: 145 | 146 | m 147 | 148 | e e 149 | m e m m 150 | e d d A 151 | e md e m m 152 | e e e 153 | m 154 | e e 155 | d d 156 | e m e 157 | 158 | e e d d d 159 | m 160 | d 161 | e d 162 | e 163 | m 164 | e e d d 165 | 166 | e m 167 | d d d 168 | e e e e 169 | m 170 | e 171 | e md d e dm 172 | 173 | m m 174 | e e d d d 175 | m 176 | m e e 177 | d m e e 178 | e e 179 | m 180 | e e e e 181 | m 182 | e e 183 | m e e 184 | e e 185 | m 186 | d d 187 | m 188 | 189 | 190 | 191 | X 192 | e e e e e e 193 | 194 | m 195 | 196 | e d d 197 | 198 | e 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /maps/level2.map: -------------------------------------------------------------------------------- 1 | wall5 {1.00,0.75,0.00,0.25} 2 | wall2 {0.75,0.50,0.50,0.75} 3 | wall6 {1.00,0.75,0.75,1.00} 4 | wall9 {0.50,0.25,0.00,0.25} 5 | wall4 {0.75,0.50,0.00,0.25} 6 | wall3 {0.25,0.00,0.25,0.50} 7 | wall12 {0.50,0.25,0.50,0.75} 8 | wall7 {1.00,0.75,0.50,0.75} 9 | wall10 {0.25,0.00,0.75,1.00} 10 | wall11 {0.50,0.25,0.75,1.00} 11 | wall1 {0.25,0.00,0.00,0.25} 12 | wall8 {1.00,0.75,0.25,0.50} 13 | lengthmap 064 14 | MAP: 15 | 16 | 17 | 111111111 333333333333333333 18 | 111 11334133413341334133 19 | 111 34 14 14 14 13 20 | 1 31 41 41 41 43 21 | 11 331433143314331433 22 | 1 15151 333333333333333333 23 | 1 33111 7 24 | 1 3111171 777777777777777777 25 | 1 3111 771 777777777777777777 26 | 1 33111 77 1 7 27 | 1 15151 77 1 28 | 1 77 888 888 888 29 | 1 11111 77 888 888 888 30 | 1 11119 77 888 888 888 31 | 1 1111 77 7 7 7 77777 32 | 1 9111 77 7777:777;777:777777 33 | 1 111 7777777777777777777777 34 | 1 111 77 7777:777;777:777777 35 | 1 7 77 7 7 7 77777 36 | 1 7777777777 888 888 888 37 | 1 7777777777 888 888 888 38 | 1 77 888 888 888 39 | 1 77 40 | 1 88 77 8888888 41 | 1 8881 77777777777777 8 555 8 42 | 1 88881177777777777777 8 1<1 8 43 | 1 88 8811 7 7 7 8 8 44 | 1 88 8881 111 111 111 8888888 45 | 1 8 88 8 11141114111 8888888 46 | 1 8 8888 11141414111 8888888 47 | 1 888888 414 4 4 414 1 1 48 | 1 11 14111414141114111111 49 | 1 1 81111111111111111118 50 | 11111 88811111111111111118 51 | 11111 11 888888888888888888 52 | 1 1 1 888888888888888888 53 | 1 11 111 54 | 11111 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | PLANES: 80 | 81 | 82 | 111111111 222222222222222222 83 | 111 11222222222222222222 84 | 111 22 22 22 22 22 85 | 1 22 22 22 22 22 86 | 11 222222222222222222 87 | 1 22222 222222222222222222 88 | 1 22222 6 89 | 1 2222266 666666666666666666 90 | 1 2222 666 666666666666666666 91 | 1 22222 66 1 6 92 | 1 22222 66 1 93 | 1 66 888 888 888 94 | 1 22222 66 888 888 888 95 | 1 22222 66 888 888 888 96 | 1 2222 66 6 6 6 66666 97 | 1 2222 66 6666666666666666666 98 | 1 222 6666666666666666666666 99 | 1 222 66 6666666666666666666 100 | 1 6 66 6 6 6 66666 101 | 1 6666666666 888 888 888 102 | 1 6666666666 888 888 888 103 | 1 66 888 888 888 104 | 1 66 105 | 1 22 66 8866688 106 | 1 2221 66666666666666 8 666 8 107 | 1 22221166666666666666 8 666 8 108 | 1 22 2211 6 6 6 8 8 109 | 1 22 2221 666 666 666 8888888 110 | 1 2 22 2 66666666666 8888888 111 | 1 2 2222 66666666666 8888888 112 | 1 222222 666 6 6 666 1 1 113 | 1 11 66666666666666666666 114 | 1 1 66666666666666666666 115 | 11111 66666666666666666666 116 | 11111 11 666666666666666666 117 | 1 1 1 666666666666666666 118 | 1 11 111 119 | 11111 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | SPECIALS: 145 | 146 | 147 | e 148 | 149 | 150 | 151 | 152 | m 153 | e d 154 | d 155 | 156 | e e 157 | m 158 | 159 | e 160 | m e 161 | d d d 162 | e e 163 | d A 164 | e 165 | d d d d 166 | 167 | m m 168 | 169 | 170 | 171 | 172 | X 173 | d d d 174 | 175 | e e e 176 | m 177 | d d 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/level.go: -------------------------------------------------------------------------------- 1 | /* 2 | WolfenGo - https://github.com/gdm85/wolfengo 3 | Copyright (C) 2016~2019 gdm85 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "math" 24 | 25 | "github.com/gdm85/wolfengo/src/gl" 26 | ) 27 | 28 | const ( 29 | spotWidth = 1.0 30 | spotLength = 1.0 31 | spotHeight = 1.0 32 | numTexExp = 4 33 | openDistance = 1.0 34 | doorOpenMovementAmount = 0.9 35 | ) 36 | 37 | var numTextures = uint32(math.Pow(2, numTexExp)) 38 | 39 | type Level struct { 40 | mesh Mesh 41 | level *Map 42 | shader *Shader 43 | material *Material 44 | transform *Transform 45 | player *Player 46 | doors []*Door 47 | monsters []*Monster 48 | medkits []*Medkit 49 | medkitsToRemove []*Medkit 50 | exitPoints []*Vector3f 51 | collisionPosStart, collisionPosEnd []*Vector2f 52 | 53 | game *Game // parent game 54 | } 55 | 56 | var ( 57 | _basicShader *Shader 58 | collectionTexture *Texture 59 | ) 60 | 61 | func getBasicShader() (*Shader, error) { 62 | if _basicShader == nil { 63 | var err error 64 | collectionTexture, err = NewTexture("WolfCollection.png") 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | _basicShader, err = NewShader(true) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | err = _basicShader.addProgramFromFile("basicVertex"+shaderVersion+".vs", gl.VERTEX_SHADER) 75 | if err != nil { 76 | return nil, err 77 | } 78 | err = _basicShader.addProgramFromFile("basicFragment"+shaderVersion+".fs", gl.FRAGMENT_SHADER) 79 | if err != nil { 80 | return nil, err 81 | } 82 | err = _basicShader.compile() 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | err = _basicShader.addUniform("transform") 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | err = _basicShader.addUniform("color") 93 | if err != nil { 94 | return nil, err 95 | } 96 | } 97 | return _basicShader, nil 98 | } 99 | 100 | func (g *Game) NewLevel(levelNum uint) (*Level, error) { 101 | l := &Level{game: g} 102 | 103 | l.transform = l.game.NewTransform() 104 | 105 | var fileName string 106 | if debugLevelTest { 107 | fileName = "levelTest.map" 108 | } else { 109 | fileName = fmt.Sprintf("level%d.map", levelNum) 110 | } 111 | 112 | var err error 113 | l.level, err = NewMap(fileName) 114 | if err != nil { 115 | return nil, err 116 | } 117 | 118 | l.material = NewMaterial(collectionTexture) 119 | 120 | l.shader, err = getBasicShader() 121 | if err != nil { 122 | return nil, err 123 | } 124 | 125 | err = l.generate() 126 | if err != nil { 127 | return nil, err 128 | } 129 | 130 | // some validation 131 | if l.player == nil { 132 | return nil, fmt.Errorf("invalid generated level: no player set") 133 | } 134 | 135 | return l, nil 136 | } 137 | 138 | func (l *Level) openDoors(position Vector3f, tryExitLevel bool) error { 139 | for _, door := range l.doors { 140 | if door.transform.translation.sub(position).length() < openDistance { 141 | door.open() 142 | } 143 | } 144 | 145 | if tryExitLevel { 146 | for _, exitPoint := range l.exitPoints { 147 | if exitPoint.sub(position).length() < openDistance { 148 | err := l.game.loadNextLevel() 149 | if err != nil { 150 | return err 151 | } 152 | } 153 | } 154 | } 155 | 156 | return nil 157 | } 158 | 159 | func (l *Level) damagePlayer(amt int) { 160 | l.player.damage(amt) 161 | } 162 | 163 | func (l *Level) input() error { 164 | return l.player.input() 165 | } 166 | 167 | func (l *Level) update() error { 168 | for _, door := range l.doors { 169 | door.update() 170 | } 171 | 172 | l.player.update() 173 | 174 | for _, medkit := range l.medkits { 175 | medkit.update() 176 | } 177 | 178 | for _, monster := range l.monsters { 179 | err := monster.update() 180 | if err != nil { 181 | return err 182 | } 183 | } 184 | 185 | if len(l.medkitsToRemove) > 0 { 186 | newMedkits := make([]*Medkit, 0, len(l.medkits)-len(l.medkitsToRemove)) 187 | for _, m := range l.medkits { 188 | removed := false 189 | for _, r := range l.medkitsToRemove { 190 | if m == r { 191 | removed = true 192 | break 193 | } 194 | } 195 | if !removed { 196 | newMedkits = append(newMedkits, m) 197 | } 198 | } 199 | l.medkits = newMedkits 200 | } 201 | 202 | return nil 203 | } 204 | 205 | func (l *Level) removeMedkit(m *Medkit) { 206 | l.medkitsToRemove = append(l.medkitsToRemove, m) 207 | } 208 | 209 | func (l *Level) render() { 210 | l.shader.bind() 211 | 212 | l.shader.updateUniforms(l.transform.getProjectedTransformation(l.player.camera), l.material) 213 | l.mesh.draw() 214 | 215 | for _, door := range l.doors { 216 | door.render() 217 | } 218 | 219 | for _, monster := range l.monsters { 220 | monster.render() 221 | } 222 | 223 | for _, medkit := range l.medkits { 224 | medkit.render() 225 | } 226 | 227 | l.player.render() 228 | } 229 | 230 | func rectCollide(oldPos, newPos, size1, pos2, size2 Vector2f) (result Vector2f) { 231 | if newPos.X+size1.X < pos2.X || 232 | newPos.X-size1.X > pos2.X+size2.X*size2.X || 233 | oldPos.Y+size1.Y < pos2.Y || 234 | oldPos.Y-size1.Y > pos2.Y+size2.Y*size2.Y { 235 | result.X = 1 236 | } 237 | 238 | if oldPos.X+size1.X < pos2.X || 239 | oldPos.X-size1.X > pos2.X+size2.X*size2.X || 240 | newPos.Y+size1.Y < pos2.Y || 241 | newPos.Y-size1.Y > pos2.Y+size2.Y*size2.Y { 242 | result.Y = 1 243 | } 244 | 245 | return 246 | } 247 | 248 | func (l *Level) checkCollision(oldPos, newPos Vector3f, objectWidth, objectLength float32) Vector3f { 249 | collisionVector := Vector2f{1, 1} 250 | movementVector := newPos.sub(oldPos) 251 | 252 | if movementVector.length() > 0 { 253 | blockSize := Vector2f{spotWidth, spotLength} 254 | objectSize := Vector2f{objectWidth, objectLength} 255 | 256 | oldPos2 := Vector2f{oldPos.X, oldPos.Z} 257 | newPos2 := Vector2f{newPos.X, newPos.Z} 258 | 259 | for i := 0; i < l.level.width; i++ { 260 | for j := 0; j < l.level.height; j++ { 261 | if l.level.IsEmpty(i, j) { 262 | collisionVector = collisionVector.mul(rectCollide(oldPos2, newPos2, objectSize, blockSize.mul(Vector2f{float32(i), float32(j)}), blockSize)) 263 | } 264 | } 265 | } 266 | 267 | for _, door := range l.doors { 268 | doorSize := door.getSize() 269 | doorPos3f := &door.transform.translation 270 | doorPos2f := Vector2f{doorPos3f.X, doorPos3f.Z} 271 | collisionVector = collisionVector.mul(rectCollide(oldPos2, newPos2, objectSize, doorPos2f, doorSize)) 272 | } 273 | } 274 | 275 | return Vector3f{collisionVector.X, 0, collisionVector.Y} 276 | } 277 | 278 | func (l *Level) checkIntersections(lineStart, lineEnd Vector2f, hurtMonsters bool) *Vector2f { 279 | var nearestIntersection *Vector2f 280 | 281 | for i := 0; i < len(l.collisionPosStart); i++ { 282 | collisionVector := lineIntersect(lineStart, lineEnd, *l.collisionPosStart[i], *l.collisionPosEnd[i]) 283 | nearestIntersection = findNearestVector2f(nearestIntersection, collisionVector, lineStart) 284 | } 285 | 286 | // doors stop bullets 287 | for _, door := range l.doors { 288 | doorPos3f := door.transform.translation 289 | doorPos2f := Vector2f{doorPos3f.X, doorPos3f.Z} 290 | collisionVector := lineIntersectRect(lineStart, lineEnd, doorPos2f, door.getSize()) 291 | 292 | nearestIntersection = findNearestVector2f(nearestIntersection, collisionVector, lineStart) 293 | } 294 | 295 | if hurtMonsters { 296 | var nearestMonsterIntersect *Vector2f 297 | var nearestMonster *Monster 298 | 299 | for _, monster := range l.monsters { 300 | monsterPos3f := monster.transform.translation 301 | monsterPos2f := Vector2f{monsterPos3f.X, monsterPos3f.Z} 302 | collisionVector := lineIntersectRect(lineStart, lineEnd, monsterPos2f, defaultMonsterSize) 303 | 304 | nearestMonsterIntersect = findNearestVector2f(nearestMonsterIntersect, collisionVector, lineStart) 305 | 306 | if nearestMonsterIntersect == collisionVector { 307 | nearestMonster = monster 308 | } 309 | } 310 | 311 | if nearestMonsterIntersect != nil && (nearestIntersection == nil || 312 | nearestMonsterIntersect.sub(lineStart).length() < nearestIntersection.sub(lineStart).length()) { 313 | if nearestMonster != nil { 314 | nearestMonster.damage(getPlayerDamage()) 315 | } 316 | } 317 | } 318 | 319 | return nearestIntersection 320 | } 321 | 322 | func findNearestVector2f(a, b *Vector2f, positionRelativeTo Vector2f) *Vector2f { 323 | if b != nil && (a == nil || 324 | a.sub(positionRelativeTo).length() > b.sub(positionRelativeTo).length()) { 325 | return b 326 | } 327 | 328 | return a 329 | } 330 | 331 | func lineIntersectRect(lineStart, lineEnd, rectPos, rectSize Vector2f) (result *Vector2f) { 332 | collisionVector := lineIntersect(lineStart, lineEnd, rectPos, Vector2f{rectPos.X + rectSize.X, rectPos.Y}) 333 | result = findNearestVector2f(result, collisionVector, lineStart) 334 | 335 | collisionVector = lineIntersect(lineStart, lineEnd, rectPos, Vector2f{rectPos.X, rectPos.Y + rectSize.Y}) 336 | result = findNearestVector2f(result, collisionVector, lineStart) 337 | 338 | collisionVector = lineIntersect(lineStart, lineEnd, Vector2f{rectPos.X, rectPos.Y + rectSize.Y}, rectPos.add(rectSize)) 339 | result = findNearestVector2f(result, collisionVector, lineStart) 340 | 341 | collisionVector = lineIntersect(lineStart, lineEnd, Vector2f{rectPos.X + rectSize.X, rectPos.Y}, rectPos.add(rectSize)) 342 | result = findNearestVector2f(result, collisionVector, lineStart) 343 | 344 | return 345 | } 346 | 347 | func vector2fCross(a, b Vector2f) float32 { 348 | return a.X*b.Y - a.Y*b.X 349 | } 350 | 351 | //http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect 352 | func lineIntersect(lineStart1, lineEnd1, lineStart2, lineEnd2 Vector2f) *Vector2f { 353 | line1 := lineEnd1.sub(lineStart1) 354 | line2 := lineEnd2.sub(lineStart2) 355 | 356 | cross := vector2fCross(line1, line2) 357 | 358 | if cross == 0 { 359 | return nil 360 | } 361 | 362 | distanceBetweenLineStarts := lineStart2.sub(lineStart1) 363 | 364 | a := vector2fCross(distanceBetweenLineStarts, line2) / cross 365 | b := vector2fCross(distanceBetweenLineStarts, line1) / cross 366 | 367 | if 0.0 < a && a < 1.0 && 0.0 < b && b < 1.0 { 368 | result := lineStart1.add(line1.mulf(a)) 369 | return &result 370 | } 371 | 372 | return nil 373 | } 374 | 375 | func (l *Level) generate() error { 376 | var vertices []*Vertex 377 | var indices []int32 378 | 379 | for i := 0; i < l.level.width; i++ { 380 | for j := 0; j < l.level.height; j++ { 381 | if l.level.IsEmpty(i, j) { 382 | continue 383 | } 384 | 385 | err := l.addSpecial(Special(l.level.specials[i][j]), i, j) 386 | if err != nil { 387 | return err 388 | } 389 | 390 | //Generate Floor 391 | texCoords := l.level.PlaneTexCoords(i, j) 392 | addFace(&indices, len(vertices), true) 393 | v, err := addVertices(i, j, 0, true, false, true, texCoords[:]) 394 | if err != nil { 395 | return err 396 | } 397 | vertices = append(vertices, v...) 398 | 399 | //Generate Ceiling 400 | addFace(&indices, len(vertices), false) 401 | v, err = addVertices(i, j, 1, true, false, true, texCoords[:]) 402 | if err != nil { 403 | return err 404 | } 405 | vertices = append(vertices, v...) 406 | 407 | //Generate Walls 408 | texCoords = l.level.WallTexCoords(i, j) 409 | 410 | if l.level.IsEmpty(i, j-1) { 411 | l.collisionPosStart = append(l.collisionPosStart, &Vector2f{float32(i * spotWidth), float32(j * spotLength)}) 412 | l.collisionPosEnd = append(l.collisionPosEnd, &Vector2f{float32((i + 1) * spotWidth), float32(j * spotLength)}) 413 | addFace(&indices, len(vertices), false) 414 | v, err := addVertices(i, 0, j, true, true, false, texCoords[:]) 415 | if err != nil { 416 | return err 417 | } 418 | vertices = append(vertices, v...) 419 | } 420 | if l.level.IsEmpty(i, j+1) { 421 | l.collisionPosStart = append(l.collisionPosStart, &Vector2f{float32(i * spotWidth), float32((j + 1) * spotLength)}) 422 | l.collisionPosEnd = append(l.collisionPosEnd, &Vector2f{float32((i + 1) * spotWidth), float32((j + 1) * spotLength)}) 423 | addFace(&indices, len(vertices), true) 424 | v, err := addVertices(i, 0, j+1, true, true, false, texCoords[:]) 425 | if err != nil { 426 | return err 427 | } 428 | vertices = append(vertices, v...) 429 | } 430 | 431 | if l.level.IsEmpty(i-1, j) { 432 | l.collisionPosStart = append(l.collisionPosStart, &Vector2f{float32(i * spotWidth), float32(j * spotLength)}) 433 | l.collisionPosEnd = append(l.collisionPosEnd, &Vector2f{float32(i * spotWidth), float32((j + 1) * spotLength)}) 434 | 435 | addFace(&indices, len(vertices), true) 436 | v, err := addVertices(0, j, i, false, true, true, texCoords[:]) 437 | if err != nil { 438 | return err 439 | } 440 | vertices = append(vertices, v...) 441 | } 442 | 443 | if l.level.IsEmpty(i+1, j) { 444 | l.collisionPosStart = append(l.collisionPosStart, &Vector2f{float32((i + 1) * spotWidth), float32(j * spotLength)}) 445 | l.collisionPosEnd = append(l.collisionPosEnd, &Vector2f{float32((i + 1) * spotWidth), float32((j + 1) * spotLength)}) 446 | addFace(&indices, len(vertices), false) 447 | v, err := addVertices(0, j, i+1, false, true, true, texCoords[:]) 448 | if err != nil { 449 | return err 450 | } 451 | vertices = append(vertices, v...) 452 | } 453 | } 454 | } 455 | 456 | l.mesh = NewMesh(vertices, indices, false) 457 | return nil 458 | } 459 | 460 | func calcTexCoords(value uint8) []float32 { 461 | texX := uint32(value) / numTextures 462 | texY := texX % numTexExp 463 | texX /= numTexExp 464 | 465 | result := make([]float32, 4) 466 | 467 | result[0] = 1.0 - float32(texX)/float32(numTexExp) 468 | result[1] = result[0] - 1.0/float32(numTexExp) 469 | result[3] = 1.0 - float32(texY)/float32(numTexExp) 470 | result[2] = result[3] - 1.0/float32(numTexExp) 471 | 472 | return result 473 | } 474 | 475 | func (l *Level) addSpecial(special Special, x, y int) error { 476 | switch special { 477 | case Empty: 478 | return nil 479 | case DoorSpecial: 480 | err := l.addDoor(x, y) 481 | if err != nil { 482 | return err 483 | } 484 | case PlayerA: 485 | l.player = l.game.NewPlayer(Vector3f{(float32(x) + 0.5) * spotWidth, 0.4375, (float32(y) + 0.5) * spotLength}, defaultPlayer.mesh, defaultGunMaterial) 486 | case MonsterSpecial: 487 | monsterTransform := l.game.NewTransform() 488 | monsterTransform.translation = Vector3f{(float32(x) + 0.5) * spotWidth, 0, (float32(y) + 0.5) * spotLength} 489 | l.monsters = append(l.monsters, l.game.NewMonster(monsterTransform, _defaultMonster.animations)) 490 | case SmallMedkit: 491 | l.medkits = append(l.medkits, l.game.NewMedkit(Vector3f{(float32(x) + 0.5) * spotWidth, 0, (float32(y) + 0.5) * spotLength})) 492 | case ExitSpecial: 493 | l.exitPoints = append(l.exitPoints, &Vector3f{(float32(x) + 0.5) * spotWidth, 0, (float32(y) + 0.5) * spotLength}) 494 | default: 495 | panic(fmt.Sprintf("unrecognized blue value: %d", special)) 496 | } 497 | 498 | return nil 499 | } 500 | 501 | func addFace(indices *[]int32, _startLocation int, direction bool) { 502 | startLocation := int32(_startLocation) 503 | var add []int32 504 | if direction { 505 | add = []int32{ 506 | startLocation + 2, 507 | startLocation + 1, 508 | startLocation + 0, 509 | startLocation + 3, 510 | startLocation + 2, 511 | startLocation + 0, 512 | } 513 | } else { 514 | add = []int32{ 515 | startLocation + 0, 516 | startLocation + 1, 517 | startLocation + 2, 518 | startLocation + 0, 519 | startLocation + 2, 520 | startLocation + 3, 521 | } 522 | } 523 | 524 | *indices = append(*indices, add...) 525 | } 526 | 527 | func addVertices(_i, _j, _offset int, x, y, z bool, texCoords []float32) (result []*Vertex, err error) { 528 | i, j, offset := float32(_i), float32(_j), float32(_offset) 529 | 530 | result = make([]*Vertex, 4) 531 | if x && z { 532 | result[0] = &Vertex{Vector3f{(i * spotWidth), (offset) * spotHeight, j * spotLength}, Vector2f{texCoords[1], texCoords[3]}, Vector3f{}} 533 | result[1] = &Vertex{Vector3f{((i + 1) * spotWidth), (offset) * spotHeight, j * spotLength}, Vector2f{texCoords[0], texCoords[3]}, Vector3f{}} 534 | result[2] = &Vertex{Vector3f{((i + 1) * spotWidth), (offset) * spotHeight, (j + 1) * spotLength}, Vector2f{texCoords[0], texCoords[2]}, Vector3f{}} 535 | result[3] = &Vertex{Vector3f{(i * spotWidth), (offset) * spotHeight, (j + 1) * spotLength}, Vector2f{texCoords[1], texCoords[2]}, Vector3f{}} 536 | } else if x && y { 537 | result[0] = &Vertex{Vector3f{(i * spotWidth), j * spotHeight, offset * spotLength}, Vector2f{texCoords[1], texCoords[3]}, Vector3f{}} 538 | result[1] = &Vertex{Vector3f{((i + 1) * spotWidth), j * spotHeight, offset * spotLength}, Vector2f{texCoords[0], texCoords[3]}, Vector3f{}} 539 | result[2] = &Vertex{Vector3f{((i + 1) * spotWidth), (j + 1) * spotHeight, offset * spotLength}, Vector2f{texCoords[0], texCoords[2]}, Vector3f{}} 540 | result[3] = &Vertex{Vector3f{(i * spotWidth), (j + 1) * spotHeight, offset * spotLength}, Vector2f{texCoords[1], texCoords[2]}, Vector3f{}} 541 | } else if y && z { 542 | result[0] = &Vertex{Vector3f{(offset * spotWidth), i * spotHeight, j * spotLength}, Vector2f{texCoords[1], texCoords[3]}, Vector3f{}} 543 | result[1] = &Vertex{Vector3f{(offset * spotWidth), i * spotHeight, (j + 1) * spotLength}, Vector2f{texCoords[0], texCoords[3]}, Vector3f{}} 544 | result[2] = &Vertex{Vector3f{(offset * spotWidth), (i + 1) * spotHeight, (j + 1) * spotLength}, Vector2f{texCoords[0], texCoords[2]}, Vector3f{}} 545 | result[3] = &Vertex{Vector3f{(offset * spotWidth), (i + 1) * spotHeight, j * spotLength}, Vector2f{texCoords[1], texCoords[2]}, Vector3f{}} 546 | } else { 547 | err = fmt.Errorf("Invalid plane used in level generator") 548 | return 549 | } 550 | return 551 | } 552 | 553 | func (l *Level) addDoor(x, y int) error { 554 | doorTransform := l.game.NewTransform() 555 | 556 | xDoor := l.level.IsEmpty(x, y-1) && l.level.IsEmpty(x, y+1) 557 | yDoor := l.level.IsEmpty(x-1, y) && l.level.IsEmpty(x+1, y) 558 | 559 | if xDoor == yDoor { 560 | return fmt.Errorf("Level Generation has failed! :( You placed a door in an invalid location at %d,%d", x, y) 561 | } 562 | 563 | var openPosition *Vector3f 564 | 565 | if yDoor { 566 | doorTransform.translation = Vector3f{float32(x), 0, float32(y) + spotLength/2} 567 | t := doorTransform.translation.sub(Vector3f{doorOpenMovementAmount, 0.0, 0.0}) 568 | openPosition = &t 569 | } 570 | 571 | if xDoor { 572 | doorTransform.translation = Vector3f{float32(x) + spotWidth/2, 0, float32(y)} 573 | doorTransform.rotation = Vector3f{0, 90, 0} 574 | t := doorTransform.translation.sub(Vector3f{0.0, 0.0, doorOpenMovementAmount}) 575 | openPosition = &t 576 | } 577 | 578 | l.doors = append(l.doors, l.game.NewDoor(doorTransform, l.material, *openPosition)) 579 | return nil 580 | } 581 | --------------------------------------------------------------------------------