├── .gitignore
├── LICENSE.md
├── Makefile
├── README.md
├── actor
├── fpsActor.go
├── freeMoveActor.go
├── physicsActor.go
└── physicsActor2D.go
├── assets
├── cache.go
├── geometryUtils.go
├── importers.go
├── loader.go
├── maps.go
└── objImporter.go
├── controller
├── basicMovementController.go
├── controller.go
├── factory.go
├── fpsController.go
└── input.go
├── editor
├── editor.go
├── editorController.go
├── fileBrowser.go
├── iconData.go
├── maps.go
├── models
│ ├── map.go
│ └── node.go
├── nodeEditor.go
├── overview.go
├── progressBar.go
├── screens.go
└── ui.go
├── effects
├── particleGroup.go
├── particleSystem.go
├── particleSystem_test.go
├── sprite.go
└── util.go
├── emitter
├── emitter.go
└── emitter_test.go
├── engine
├── engine.go
├── updatable.go
└── util.go
├── examples
├── lighting
│ └── main.go
├── multiplayer
│ └── main.go
├── platformer
│ └── main.go
├── simple
│ └── main.go
└── ui
│ └── main.go
├── glfwController
├── controller.go
├── controllerManager.go
├── controller_test.go
└── inputMappings.go
├── go.mod
├── go.sum
├── libs
└── freetype
│ ├── AUTHORS
│ ├── CONTRIBUTORS
│ ├── LICENSE
│ ├── README
│ ├── cmd
│ └── print-glyph-points
│ │ └── main.c
│ ├── example
│ ├── drawer
│ │ ├── main.go
│ │ └── out.png
│ ├── freetype
│ │ ├── main.go
│ │ └── out.png
│ ├── gamma
│ │ └── main.go
│ ├── raster
│ │ └── main.go
│ ├── round
│ │ └── main.go
│ └── truetype
│ │ └── main.go
│ ├── freetype.go
│ ├── licenses
│ ├── ftl.txt
│ └── gpl.txt
│ ├── raster
│ ├── geom.go
│ ├── paint.go
│ ├── raster.go
│ └── stroke.go
│ └── truetype
│ ├── face.go
│ ├── glyph.go
│ ├── hint.go
│ ├── opcodes.go
│ └── truetype.go
├── main.go
├── networking
├── client.go
├── clock.go
├── networking.go
├── packet.go
├── packet_test.go
├── server.go
└── session.go
├── opengl
├── glRenderer.go
├── postEffects.go
└── shaders.go
├── physics
├── chipmunk
│ ├── body.go
│ └── world.go
└── physicsAPI
│ ├── characterController.go
│ ├── constraints.go
│ ├── physicsObject.go
│ └── physicsSpace.go
├── renderer
├── Cubemap.go
├── camera.go
├── fpsMeter.go
├── geometry.go
├── light.go
├── material.go
├── node.go
├── primitiveData.go
├── renderer.go
├── sceneGraph.go
└── shader.go
├── resources
├── cubemap.png
├── cubemapNightSky.jpg
├── fire.png
├── majic.png
├── smoke.png
├── space.jpg
├── spark.png
└── stickman.png
├── serializer
├── serialize.go
└── serializer.go
├── shaderBuilder
├── README.md
├── main.go
└── parser
│ ├── expression.go
│ ├── lexer.go
│ ├── parser.go
│ ├── parser_test.go
│ ├── test
│ ├── expected.frag
│ ├── expected.vert
│ ├── includeTest.glsl
│ ├── includeTest2.glsl
│ └── test.glsl
│ └── token.go
├── shaders
├── basic.glsl
├── build
│ ├── basic.frag
│ ├── basic.vert
│ ├── diffuseSpecular.frag
│ ├── diffuseSpecular.vert
│ ├── pbr.frag
│ ├── pbr.vert
│ ├── pbrComposite.frag
│ ├── pbrComposite.vert
│ └── postEffects
│ │ ├── cell.frag
│ │ ├── cell.vert
│ │ ├── glow.frag
│ │ └── glow.vert
├── diffuseSpecular.glsl
├── lib
│ ├── ambientLight.glsl
│ ├── base.glsl
│ ├── common.glsl
│ ├── directLight.glsl
│ ├── directionalLights.glsl
│ ├── fresnelEffect.glsl
│ ├── glowOutput.glsl
│ ├── indirectLight.glsl
│ ├── metalnessTexture.glsl
│ ├── pbrCompositeTextures.glsl
│ ├── pointLights.glsl
│ ├── roughnessTexture.glsl
│ ├── textures.glsl
│ └── worldTransform.glsl
├── pbr.glsl
├── pbrComposite.glsl
└── postEffects
│ ├── cell.glsl
│ └── glow.glsl
├── ui
├── children.go
├── container.go
├── defaultFont.go
├── dropdown.go
├── element.go
├── hitbox.go
├── html.go
├── htmlAssets.go
├── image.go
├── progressBar.go
├── text.go
├── textfield.go
├── uiController.go
├── window.go
└── windowAddons.go
└── util
├── image.go
├── math.go
├── mathUtils_test.go
├── serialize.go
├── serialize_test.go
└── util.go
/.gitignore:
--------------------------------------------------------------------------------
1 | /TestAssets
2 | /build
3 | *.exe
4 | tags
5 | *~
6 | *.swp
7 | debug
8 | .vscode/
9 | sBuilder
10 | resources/wellScene/
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | GoEngine - A game engine written in go, intended for 2d and 3d games.
2 | Copyright (C) 2016 Joshua Wales
3 |
4 | This program is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | COVER_DIR = cover
2 | BUILD_DIR = build
3 | SHADER_BUILD_DIR = shaders/build
4 |
5 | build: compileShaderBuilder
6 | mkdir -p $(SHADER_BUILD_DIR)
7 |
8 | ./sBuilder shaders/basic.glsl vert > $(SHADER_BUILD_DIR)/basic.vert
9 | ./sBuilder shaders/basic.glsl frag > $(SHADER_BUILD_DIR)/basic.frag
10 |
11 | ./sBuilder shaders/pbr.glsl vert > $(SHADER_BUILD_DIR)/pbr.vert
12 | ./sBuilder shaders/pbr.glsl frag > $(SHADER_BUILD_DIR)/pbr.frag
13 |
14 | ./sBuilder shaders/diffuseSpecular.glsl vert > $(SHADER_BUILD_DIR)/diffuseSpecular.vert
15 | ./sBuilder shaders/diffuseSpecular.glsl frag > $(SHADER_BUILD_DIR)/diffuseSpecular.frag
16 |
17 | ./sBuilder shaders/pbrComposite.glsl vert > $(SHADER_BUILD_DIR)/pbrComposite.vert
18 | ./sBuilder shaders/pbrComposite.glsl frag > $(SHADER_BUILD_DIR)/pbrComposite.frag
19 |
20 | mkdir -p $(SHADER_BUILD_DIR)/postEffects
21 |
22 | ./sBuilder shaders/postEffects/cell.glsl vert > $(SHADER_BUILD_DIR)/postEffects/cell.vert
23 | ./sBuilder shaders/postEffects/cell.glsl frag > $(SHADER_BUILD_DIR)/postEffects/cell.frag
24 |
25 | ./sBuilder shaders/postEffects/glow.glsl vert > $(SHADER_BUILD_DIR)/postEffects/glow.vert
26 | ./sBuilder shaders/postEffects/glow.glsl frag > $(SHADER_BUILD_DIR)/postEffects/glow.frag
27 |
28 | compileShaderBuilder:
29 | go build -o sBuilder ./shaderBuilder
30 |
31 | testintall:
32 | go get -t github.com/walesey/go-engine/actor
33 | go get -t github.com/walesey/go-engine/assets
34 | go get -t github.com/walesey/go-engine/controller
35 | go get -t github.com/walesey/go-engine/effects
36 | go get -t github.com/walesey/go-engine/emitter
37 | go get -t github.com/walesey/go-engine/engine
38 | go get -t github.com/walesey/go-engine/networking
39 | go get -t github.com/walesey/go-engine/physics/physicsAPI
40 | go get -t github.com/walesey/go-engine/shaderBuilder/parser
41 | go get -t github.com/walesey/go-engine/ui
42 | go get -t github.com/walesey/go-engine/util
43 |
44 | test: testintall
45 | go test github.com/walesey/go-engine/actor
46 | go test github.com/walesey/go-engine/assets
47 | go test github.com/walesey/go-engine/controller
48 | go test github.com/walesey/go-engine/effects
49 | go test github.com/walesey/go-engine/emitter
50 | go test github.com/walesey/go-engine/engine
51 | go test github.com/walesey/go-engine/networking
52 | go test github.com/walesey/go-engine/physics/physicsAPI
53 | go test github.com/walesey/go-engine/shaderBuilder/parser
54 | go test github.com/walesey/go-engine/ui
55 | go test github.com/walesey/go-engine/util
56 |
57 | coverage: testintall
58 | mkdir -p $(COVER_DIR)
59 | go test github.com/walesey/go-engine/actor -coverprofile=$(COVER_DIR)/actor.cover.out && \
60 | go test github.com/walesey/go-engine/assets -coverprofile=$(COVER_DIR)/assets.cover.out && \
61 | go test github.com/walesey/go-engine/controller -coverprofile=$(COVER_DIR)/controller.cover.out && \
62 | go test github.com/walesey/go-engine/effects -coverprofile=$(COVER_DIR)/effects.cover.out && \
63 | go test github.com/walesey/go-engine/emitter -coverprofile=$(COVER_DIR)/emitter.cover.out && \
64 | go test github.com/walesey/go-engine/engine -coverprofile=$(COVER_DIR)/engine.cover.out && \
65 | go test github.com/walesey/go-engine/networking -coverprofile=$(COVER_DIR)/networking.cover.out && \
66 | go test github.com/walesey/go-engine/physics/physicsAPI -coverprofile=$(COVER_DIR)/physics.cover.out && \
67 | go test github.com/walesey/go-engine/shaderBuilder/parser -coverprofile=$(COVER_DIR)/shaderBuilderParser.cover.out && \
68 | go test github.com/walesey/go-engine/ui -coverprofile=$(COVER_DIR)/ui.cover.out && \
69 | go test github.com/walesey/go-engine/util -coverprofile=$(COVER_DIR)/util.cover.out && \
70 | rm -f $(COVER_DIR)/coverage.out && \
71 | echo 'mode: set' > $(COVER_DIR)/coverage.out && \
72 | cat $(COVER_DIR)/*.cover.out | sed '/mode: set/d' >> $(COVER_DIR)/coverage.out && \
73 | go tool cover -html=$(COVER_DIR)/coverage.out -o=$(COVER_DIR)/coverage.html && \
74 | rm $(COVER_DIR)/*.cover.out
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GoEngine
2 |
3 | Go engine is a simple game engine intended for 2D or 3D games.
4 |
5 | ## Features
6 |
7 | - OpenGL renderer
8 | - Obj importer
9 | - Lighting Engine
10 | - Particle System
11 | - UI system
12 | - Controller system (mouse, keyboard, joystick)
13 | - Multiplayer networking library
14 |
15 | ## Platform Support
16 |
17 | - Windows
18 | - macOS
19 |
20 | ## Instructions
21 |
22 | Example programs can be found in `examples/*`.
23 |
24 | Installing deps:
25 | sudo apt-get install mesa-utils
26 | sudo apt install mesa-common-dev
27 | sudo apt-get install libx11-dev
28 | sudo apt-get install libglfw3
29 | sudo apt-get install libxrandr-dev libxcursor-dev libxinerama-dev libxi-dev
30 | sudo apt-get install libxxf86vm-dev
31 |
32 | ## Core Packages
33 |
34 | - renderer - package contains common renderer interface and scenegraph implementation.
35 | - opengl - package contains opengl renderer implementation.
36 | - engine - package Is the high level engine interface that handles a lot of boilerplate stuff.
37 | - controller - package Is the api for keyboard/mouse/joystick controllers. (see examples/simple/main.go)
38 | - assets - asset management for images and obj files.
39 |
40 | ## Important Interfaces and Structs
41 |
42 | - renderer.Entity (interface) - anything that can be moved, rotated and scaled. (eg. Camera/Node/ParticleEmitter)
43 | - renderer.Spatial (interface) - something that can be Drawn by a Renderer (eg. Geometry/Node)
44 | - renderer.Node (struct) - Container for Spatials.
45 | - renderer.Geometry (struct) - A collection of faces and verticies.
46 | - renderer.Material (struct) - used for texturing a geometry.
47 | - renderer.Camera (struct) - Struct used to manage the camera.
48 | - renderer.Light (struct) - Struct used to manage dynamic lights.
49 | - controller.Controller (interface) - Can have (mouse/keyboard...) events bound to.
50 | - engine.Engine (interface) - The main game engine interface
51 | - engine.Updatable (interface) - anything that can be updated every game simulation step.
52 |
53 | 
54 |
--------------------------------------------------------------------------------
/actor/fpsActor.go:
--------------------------------------------------------------------------------
1 | package actor
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/physics/physicsAPI"
6 | "github.com/walesey/go-engine/renderer"
7 | )
8 |
9 | //an actor that can move around bound by physics (gravity), can jump, can walk/run
10 | type FPSActor struct {
11 | Entity renderer.Entity
12 | Character physicsAPI.CharacterController
13 | MoveSpeed, SprintSpeed float32
14 | LookSpeed float32
15 | lookPitch, lookAngle float32
16 | forwardMove, strafeMove float32
17 | walkDirection mgl32.Vec3
18 | }
19 |
20 | func NewFPSActor(entity renderer.Entity, character physicsAPI.CharacterController) *FPSActor {
21 | return &FPSActor{
22 | Entity: entity,
23 | Character: character,
24 | MoveSpeed: 0.3,
25 | SprintSpeed: 0.5,
26 | LookSpeed: 0.001,
27 | }
28 | }
29 |
30 | func (actor *FPSActor) Update(dt float64) {
31 | // orientation
32 | vertRot := mgl32.QuatRotate(actor.lookAngle, mgl32.Vec3{0, 1, 0})
33 | axis := vertRot.Rotate(mgl32.Vec3{1, 0, 0}).Cross(mgl32.Vec3{0, 1, 0})
34 | horzRot := mgl32.QuatRotate(actor.lookPitch, axis)
35 | orientation := horzRot.Mul(vertRot)
36 | actor.Entity.SetOrientation(orientation)
37 |
38 | // walking direction
39 | if actor.Character.OnGround() {
40 | actor.walkDirection = orientation.Rotate(mgl32.Vec3{actor.forwardMove, 0, actor.strafeMove})
41 | }
42 | actor.Character.SetWalkDirection(actor.walkDirection)
43 | actor.Entity.SetTranslation(actor.Character.GetPosition())
44 | }
45 |
46 | func (actor *FPSActor) Look(dx, dy float32) {
47 | actor.lookAngle = actor.lookAngle - actor.LookSpeed*dx
48 | actor.lookPitch = actor.lookPitch - actor.LookSpeed*dy
49 | if actor.lookPitch > 1.5 {
50 | actor.lookPitch = 1.5
51 | }
52 | if actor.lookPitch < -1.5 {
53 | actor.lookPitch = -1.5
54 | }
55 | }
56 |
57 | func (actor *FPSActor) StartMovingForward() {
58 | actor.forwardMove = actor.MoveSpeed
59 | }
60 |
61 | func (actor *FPSActor) StartMovingBackward() {
62 | actor.forwardMove = -actor.MoveSpeed
63 | }
64 |
65 | func (actor *FPSActor) StartStrafingLeft() {
66 | actor.strafeMove = -actor.MoveSpeed
67 | }
68 |
69 | func (actor *FPSActor) StartStrafingRight() {
70 | actor.strafeMove = actor.MoveSpeed
71 | }
72 |
73 | func (actor *FPSActor) StopMovingForward() {
74 | actor.forwardMove = 0
75 | }
76 |
77 | func (actor *FPSActor) StopMovingBackward() {
78 | actor.forwardMove = 0
79 | }
80 |
81 | func (actor *FPSActor) StopStrafingLeft() {
82 | actor.strafeMove = 0
83 | }
84 |
85 | func (actor *FPSActor) StopStrafingRight() {
86 | actor.strafeMove = 0
87 | }
88 |
89 | func (actor *FPSActor) Jump() {
90 | if actor.Character.CanJump() {
91 | actor.Character.Jump()
92 | }
93 | }
94 |
95 | func (actor *FPSActor) StandUp() {
96 |
97 | }
98 |
99 | func (actor *FPSActor) Crouch() {
100 |
101 | }
102 |
103 | func (actor *FPSActor) Prone() {
104 |
105 | }
106 |
107 | func (actor *FPSActor) StartSprinting() {
108 |
109 | }
110 |
111 | func (actor *FPSActor) StopSprinting() {
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/actor/freeMoveActor.go:
--------------------------------------------------------------------------------
1 | package actor
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/renderer"
6 | )
7 |
8 | //an actor that can move around freely in space
9 | type FreeMoveActor struct {
10 | Entity renderer.Entity
11 | Location mgl32.Vec3
12 | forwardMove, strafeMove float32
13 | lookPitch, lookAngle float32
14 | MoveSpeed, LookSpeed float32
15 | }
16 |
17 | func NewFreeMoveActor(entity renderer.Entity) *FreeMoveActor {
18 | return &FreeMoveActor{
19 | Entity: entity,
20 | lookAngle: 0.0,
21 | lookPitch: 0.0,
22 | MoveSpeed: 10.0,
23 | LookSpeed: 0.001,
24 | }
25 | }
26 |
27 | func (actor *FreeMoveActor) Update(dt float64) {
28 |
29 | //orientation
30 | vertRot := mgl32.QuatRotate(actor.lookAngle, mgl32.Vec3{0, 1, 0})
31 | axis := vertRot.Rotate(mgl32.Vec3{1, 0, 0}).Cross(mgl32.Vec3{0, 1, 0})
32 | horzRot := mgl32.QuatRotate(actor.lookPitch, axis)
33 | orientation := horzRot.Mul(vertRot)
34 | velocity := orientation.Rotate(mgl32.Vec3{actor.forwardMove, 0, actor.strafeMove})
35 | actor.Location = actor.Location.Add(velocity.Mul(float32(dt)))
36 |
37 | //update entity
38 | actor.Entity.SetTranslation(actor.Location)
39 | actor.Entity.SetOrientation(orientation)
40 | }
41 |
42 | func (actor *FreeMoveActor) Look(dx, dy float32) {
43 | actor.lookAngle = actor.lookAngle - actor.LookSpeed*dx
44 | actor.lookPitch = actor.lookPitch - actor.LookSpeed*dy
45 | if actor.lookPitch > 1.5 {
46 | actor.lookPitch = 1.5
47 | }
48 | if actor.lookPitch < -1.5 {
49 | actor.lookPitch = -1.5
50 | }
51 | }
52 |
53 | func (actor *FreeMoveActor) StartMovingUp() {
54 | actor.forwardMove = actor.MoveSpeed
55 | }
56 |
57 | func (actor *FreeMoveActor) StartMovingDown() {
58 | actor.forwardMove = -actor.MoveSpeed
59 | }
60 |
61 | func (actor *FreeMoveActor) StartMovingLeft() {
62 | actor.strafeMove = -actor.MoveSpeed
63 | }
64 |
65 | func (actor *FreeMoveActor) StartMovingRight() {
66 | actor.strafeMove = actor.MoveSpeed
67 | }
68 |
69 | func (actor *FreeMoveActor) StopMovingUp() {
70 | actor.forwardMove = 0
71 | }
72 |
73 | func (actor *FreeMoveActor) StopMovingDown() {
74 | actor.forwardMove = 0
75 | }
76 |
77 | func (actor *FreeMoveActor) StopMovingLeft() {
78 | actor.strafeMove = 0
79 | }
80 |
81 | func (actor *FreeMoveActor) StopMovingRight() {
82 | actor.strafeMove = 0
83 | }
84 |
--------------------------------------------------------------------------------
/actor/physicsActor.go:
--------------------------------------------------------------------------------
1 | package actor
2 |
3 | import (
4 | "github.com/walesey/go-engine/physics/physicsAPI"
5 | "github.com/walesey/go-engine/renderer"
6 | )
7 |
8 | type PhysicsActor struct {
9 | Entity renderer.Entity
10 | Object physicsAPI.PhysicsObject
11 | }
12 |
13 | func NewPhysicsActor(entity renderer.Entity, object physicsAPI.PhysicsObject) *PhysicsActor {
14 | return &PhysicsActor{
15 | Entity: entity,
16 | Object: object,
17 | }
18 | }
19 |
20 | func (actor *PhysicsActor) Update(dt float64) {
21 | //update entity\
22 | actor.Entity.SetTranslation(actor.Object.GetPosition())
23 | actor.Entity.SetOrientation(actor.Object.GetOrientation())
24 | }
25 |
--------------------------------------------------------------------------------
/actor/physicsActor2D.go:
--------------------------------------------------------------------------------
1 | package actor
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/physics/physicsAPI"
6 | "github.com/walesey/go-engine/renderer"
7 | )
8 |
9 | type PhysicsActor2D struct {
10 | Entity renderer.Entity
11 | Object physicsAPI.PhysicsObject2D
12 | Mask mgl32.Vec3
13 | }
14 |
15 | func NewPhysicsActor2D(entity renderer.Entity, object physicsAPI.PhysicsObject2D, mask mgl32.Vec3) *PhysicsActor2D {
16 | return &PhysicsActor2D{
17 | Entity: entity,
18 | Object: object,
19 | Mask: mask,
20 | }
21 | }
22 |
23 | func (actor *PhysicsActor2D) Update(dt float64) {
24 | objPos := actor.Object.GetPosition()
25 | var position mgl32.Vec3
26 | var orientation mgl32.Quat
27 | if actor.Mask.X() < actor.Mask.Y() && actor.Mask.X() < actor.Mask.Z() { // YZ plane
28 | position = mgl32.Vec3{0, actor.Mask.Y() * objPos.X(), actor.Mask.Z() * objPos.Y()}
29 | orientation = mgl32.QuatRotate(actor.Object.GetAngle(), mgl32.Vec3{1, 0, 0})
30 | } else if actor.Mask.Y() < actor.Mask.X() && actor.Mask.Y() < actor.Mask.Z() { // XZ plane
31 | position = mgl32.Vec3{actor.Mask.X() * objPos.X(), 0, actor.Mask.Z() * objPos.Y()}
32 | orientation = mgl32.QuatRotate(actor.Object.GetAngle(), mgl32.Vec3{0, 1, 0})
33 | } else { // XY plane
34 | position = mgl32.Vec3{actor.Mask.X() * objPos.X(), actor.Mask.Y() * objPos.Y(), 0}
35 | orientation = mgl32.QuatRotate(actor.Object.GetAngle(), mgl32.Vec3{0, 0, 1})
36 | }
37 | actor.Entity.SetTranslation(position)
38 | actor.Entity.SetOrientation(orientation)
39 | }
40 |
--------------------------------------------------------------------------------
/assets/cache.go:
--------------------------------------------------------------------------------
1 | package assets
2 |
3 | import (
4 | "image"
5 | "sync"
6 |
7 | "github.com/walesey/go-engine/renderer"
8 | )
9 |
10 | type AssetCache struct {
11 | geometries map[string]*renderer.Geometry
12 | materials map[string]*renderer.Material
13 | images map[string]image.Image
14 | fileMutexes map[string]*sync.Mutex
15 | mutex *sync.Mutex
16 | }
17 |
18 | var globalCache *AssetCache
19 |
20 | func init() {
21 | globalCache = NewAssetCache()
22 | }
23 |
24 | func (ac *AssetCache) ImportObj(path string) (geometry *renderer.Geometry, material *renderer.Material, err error) {
25 | ac.lockFilepath(path)
26 | var okGeom, okMat bool
27 | geometry, okGeom = ac.geometries[path]
28 | material, okMat = ac.materials[path]
29 | if !okGeom && !okMat {
30 | geometry, material, err = ImportObj(path)
31 | ac.mutex.Lock()
32 | ac.geometries[path] = geometry
33 | ac.materials[path] = material
34 | ac.mutex.Unlock()
35 | }
36 | ac.unlockFilepath(path)
37 | return
38 | }
39 |
40 | func (ac *AssetCache) ImportImage(path string) (img image.Image, err error) {
41 | ac.lockFilepath(path)
42 | var ok bool
43 | img, ok = ac.images[path]
44 | if !ok {
45 | img, err = ImportImage(path)
46 | ac.mutex.Lock()
47 | ac.images[path] = img
48 | ac.mutex.Unlock()
49 | }
50 | ac.unlockFilepath(path)
51 | return
52 | }
53 |
54 | func (ac *AssetCache) fileMutex(path string) *sync.Mutex {
55 | ac.mutex.Lock()
56 | if _, ok := ac.fileMutexes[path]; !ok {
57 | ac.fileMutexes[path] = &sync.Mutex{}
58 | }
59 | ac.mutex.Unlock()
60 | return ac.fileMutexes[path]
61 | }
62 |
63 | func (ac *AssetCache) lockFilepath(path string) {
64 | ac.fileMutex(path).Lock()
65 | }
66 |
67 | func (ac *AssetCache) unlockFilepath(path string) {
68 | ac.fileMutex(path).Unlock()
69 | }
70 |
71 | func ImportImageCached(path string) (image.Image, error) {
72 | return globalCache.ImportImage(path)
73 | }
74 |
75 | func ImportObjCached(path string) (geometry *renderer.Geometry, material *renderer.Material, err error) {
76 | return globalCache.ImportObj(path)
77 | }
78 |
79 | func NewAssetCache() *AssetCache {
80 | return &AssetCache{
81 | geometries: make(map[string]*renderer.Geometry),
82 | materials: make(map[string]*renderer.Material),
83 | images: make(map[string]image.Image),
84 | fileMutexes: make(map[string]*sync.Mutex),
85 | mutex: &sync.Mutex{},
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/assets/geometryUtils.go:
--------------------------------------------------------------------------------
1 | package assets
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/renderer"
6 | "github.com/walesey/go-engine/util"
7 | )
8 |
9 | // IntersectGeometry - returns a list of line segments resulting from the xz plane intersection of the geometry
10 | func IntersectGeometry(geometry *renderer.Geometry) [][2]mgl32.Vec2 {
11 | segments := make([][2]mgl32.Vec2, 0)
12 | for i := 0; i < len(geometry.Indicies); i = i + 3 {
13 | index := geometry.Indicies[i]
14 | v1 := mgl32.Vec3{geometry.Verticies[index*renderer.VertexStride], geometry.Verticies[index*renderer.VertexStride+1], geometry.Verticies[index*renderer.VertexStride+2]}
15 | index = geometry.Indicies[i+1]
16 | v2 := mgl32.Vec3{geometry.Verticies[index*renderer.VertexStride], geometry.Verticies[index*renderer.VertexStride+1], geometry.Verticies[index*renderer.VertexStride+2]}
17 | index = geometry.Indicies[i+2]
18 | v3 := mgl32.Vec3{geometry.Verticies[index*renderer.VertexStride], geometry.Verticies[index*renderer.VertexStride+1], geometry.Verticies[index*renderer.VertexStride+2]}
19 |
20 | va, vb, vc := v1, v2, v3
21 | if (v1.Y() < 0 && v2.Y() > 0 && v3.Y() > 0) || (v1.Y() > 0 && v2.Y() < 0 && v3.Y() < 0) {
22 | va, vb, vc = v1, v2, v3
23 | } else if (v1.Y() > 0 && v2.Y() < 0 && v3.Y() > 0) || (v1.Y() < 0 && v2.Y() > 0 && v3.Y() < 0) {
24 | va, vb, vc = v2, v1, v3
25 | } else if (v1.Y() > 0 && v2.Y() > 0 && v3.Y() < 0) || (v1.Y() < 0 && v2.Y() < 0 && v3.Y() > 0) {
26 | va, vb, vc = v3, v2, v1
27 | } else {
28 | continue
29 | }
30 |
31 | t_ab := -va.Y() / (va.Y() - vb.Y())
32 | t_ac := -va.Y() / (va.Y() - vc.Y())
33 | segments = append(segments, [2]mgl32.Vec2{
34 | mgl32.Vec2{va.X() + (va.X()-vb.X())*t_ab, va.Z() + (va.Z()-vb.Z())*t_ab},
35 | mgl32.Vec2{va.X() + (va.X()-vc.X())*t_ac, va.Z() + (va.Z()-vc.Z())*t_ac},
36 | })
37 | }
38 | return segments
39 | }
40 |
41 | // Converts a geometry directly into points (does threshold culling optimisation)
42 | func PointsFromGeometry(geometry *renderer.Geometry, cullThreshold float32) *[]mgl32.Vec3 {
43 | verticies := make([]mgl32.Vec3, 0, len(geometry.Indicies))
44 | for i := 0; i < len(geometry.Indicies); i = i + 1 {
45 | index := geometry.Indicies[i]
46 | v := mgl32.Vec3{
47 | geometry.Verticies[index*renderer.VertexStride],
48 | geometry.Verticies[index*renderer.VertexStride+1],
49 | geometry.Verticies[index*renderer.VertexStride+2],
50 | }
51 | //do culling
52 | include := true
53 | for _, vert := range verticies {
54 | if util.Vec3LenSq(vert.Sub(v)) < cullThreshold*cullThreshold {
55 | include = false
56 | break
57 | }
58 | }
59 | if include {
60 | verticies = append(verticies, v)
61 | }
62 | }
63 | return &verticies
64 | }
65 |
--------------------------------------------------------------------------------
/assets/importers.go:
--------------------------------------------------------------------------------
1 | package assets
2 |
3 | import (
4 | "fmt"
5 | "image"
6 | _ "image/jpeg"
7 | _ "image/png"
8 | "io"
9 | "os"
10 |
11 | "io/ioutil"
12 |
13 | "github.com/disintegration/imaging"
14 | "github.com/walesey/go-engine/renderer"
15 | )
16 |
17 | func ImportImage(file string) (image.Image, error) {
18 | imgFile, err := os.Open(file)
19 | if err != nil {
20 | fmt.Printf("Error opening image file: %v\n", err)
21 | return nil, err
22 | }
23 | return DecodeImage(imgFile)
24 | }
25 |
26 | func DecodeImage(data io.Reader) (image.Image, error) {
27 | img, _, err := image.Decode(data)
28 | if err != nil {
29 | fmt.Printf("Error decoding geometry file: %v\n", err)
30 | return nil, err
31 | }
32 | img = imaging.FlipV(img)
33 | return img, nil
34 | }
35 |
36 | func ImportShader(vertexFile, fragmentFile string) (*renderer.Shader, error) {
37 | vertsrc, err := ioutil.ReadFile(vertexFile)
38 | if err != nil {
39 | fmt.Printf("Error vertex file: %v\n", err)
40 | return nil, err
41 | }
42 |
43 | fragsrc, err := ioutil.ReadFile(fragmentFile)
44 | if err != nil {
45 | fmt.Printf("Error fragment file: %v\n", err)
46 | return nil, err
47 | }
48 |
49 | shader := renderer.NewShader()
50 | shader.VertSrc = string(vertsrc)
51 | shader.FragSrc = string(fragsrc)
52 | return shader, nil
53 | }
54 |
--------------------------------------------------------------------------------
/assets/loader.go:
--------------------------------------------------------------------------------
1 | package assets
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/walesey/go-engine/editor/models"
7 | "github.com/walesey/go-engine/renderer"
8 | )
9 |
10 | type geomImport struct {
11 | geometry *renderer.Geometry
12 | material *renderer.Material
13 | callback func(geometry *renderer.Geometry, material *renderer.Material)
14 | }
15 |
16 | type mapImport struct {
17 | node *renderer.Node
18 | model *editorModels.NodeModel
19 | callback func(node *renderer.Node, model *editorModels.NodeModel)
20 | }
21 |
22 | // The loader allows asyncronous loading of obj and map files
23 | type Loader struct {
24 | geoms chan geomImport
25 | maps chan mapImport
26 | }
27 |
28 | func NewLoader() *Loader {
29 | return &Loader{
30 | geoms: make(chan geomImport, 256),
31 | maps: make(chan mapImport, 256),
32 | }
33 | }
34 |
35 | func (loader *Loader) Update(dt float64) {
36 | for {
37 | select {
38 | case g := <-loader.geoms:
39 | g.callback(g.geometry, g.material)
40 | case m := <-loader.maps:
41 | m.callback(m.node, m.model)
42 | default:
43 | return
44 | }
45 | }
46 | }
47 |
48 | func (loader *Loader) LoadMap(path string, callback func(node *renderer.Node, model *editorModels.NodeModel)) {
49 | go func() {
50 | srcModel := LoadMap(path)
51 | destNode := renderer.NewNode()
52 | loadedModel := LoadMapToNode(srcModel.Root, destNode)
53 | loader.maps <- mapImport{
54 | node: destNode,
55 | model: loadedModel,
56 | callback: callback,
57 | }
58 | }()
59 | }
60 |
61 | func (loader *Loader) LoadObj(path string, callback func(geometry *renderer.Geometry, material *renderer.Material)) {
62 | go func() {
63 | loadedGeometry, loadedMaterial, err := ImportObjCached(path)
64 | if err != nil {
65 | log.Println("Error Loading Obj: ", err)
66 | } else {
67 | loader.geoms <- geomImport{
68 | geometry: loadedGeometry,
69 | material: loadedMaterial,
70 | callback: callback,
71 | }
72 | }
73 | }()
74 | }
75 |
--------------------------------------------------------------------------------
/assets/maps.go:
--------------------------------------------------------------------------------
1 | package assets
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 |
9 | "github.com/walesey/go-engine/editor/models"
10 | "github.com/walesey/go-engine/renderer"
11 | )
12 |
13 | func LoadMap(path string) *editorModels.MapModel {
14 | data, err := ioutil.ReadFile(path)
15 | if err != nil {
16 | log.Printf("Error Reading map file: %v\n", err)
17 | return nil
18 | }
19 |
20 | var mapModel editorModels.MapModel
21 | err = json.Unmarshal(data, &mapModel)
22 | if err != nil {
23 | log.Printf("Error unmarshaling map model: %v\n", err)
24 | return nil
25 | }
26 |
27 | return &mapModel
28 | }
29 |
30 | func LoadMapToNode(srcModel *editorModels.NodeModel, destNode *renderer.Node) *editorModels.NodeModel {
31 | copy := srcModel.Copy(func(name string) string { return name })
32 | loadMapRecursive(copy, srcModel, destNode)
33 | return copy
34 | }
35 |
36 | func loadMapRecursive(model, srcModel *editorModels.NodeModel, destNode *renderer.Node) {
37 | model.SetNode(destNode)
38 | if model.Geometry != nil {
39 | geometry, material, err := ImportObjCached(*model.Geometry)
40 | if err == nil {
41 | destNode.Add(geometry)
42 | destNode.Material = material
43 | }
44 | }
45 | destNode.SetScale(model.Scale)
46 | destNode.SetTranslation(model.Translation)
47 | destNode.SetOrientation(model.Orientation)
48 | if model.Reference != nil {
49 | copyRef(model, srcModel)
50 | }
51 | for _, childModel := range model.Children {
52 | newNode := renderer.NewNode()
53 | destNode.Add(newNode)
54 | loadMapRecursive(childModel, srcModel, newNode)
55 | }
56 | }
57 |
58 | func copyRef(model, srcModel *editorModels.NodeModel) {
59 | if model.Reference != nil {
60 | if refModel := FindNodeById(*model.Reference, srcModel); refModel != nil {
61 | for _, childModel := range refModel.Children {
62 | childCopy := childModel.Copy(func(name string) string {
63 | return fmt.Sprintf("%v::%v", *model.Reference, name)
64 | })
65 | model.Children = append(model.Children, childCopy)
66 | }
67 | model.Reference = nil
68 | }
69 | }
70 | for _, child := range model.Children {
71 | copyRef(child, srcModel)
72 | }
73 | }
74 |
75 | func FindNodeById(nodeId string, model *editorModels.NodeModel) *editorModels.NodeModel {
76 | if model.Id == nodeId {
77 | return model
78 | }
79 | for _, childModel := range model.Children {
80 | node := FindNodeById(nodeId, childModel)
81 | if node != nil {
82 | return node
83 | }
84 | }
85 | return nil
86 | }
87 |
88 | func FindNodeByClass(class string, model *editorModels.NodeModel) []*editorModels.NodeModel {
89 | return findNodeByClass(class, model, model)
90 | }
91 |
92 | func findNodeByClass(class string, model, srcModel *editorModels.NodeModel) []*editorModels.NodeModel {
93 | results := []*editorModels.NodeModel{}
94 | if hasClass(class, model) {
95 | results = append(results, model)
96 | }
97 | for _, childModel := range model.Children {
98 | results = append(results, findNodeByClass(class, childModel, srcModel)...)
99 | }
100 | if model.Reference != nil {
101 | if refModel := FindNodeById(*model.Reference, srcModel); refModel != nil {
102 | results = append(results, findNodeByClass(class, refModel, srcModel)...)
103 | }
104 | }
105 | return results
106 | }
107 |
108 | func hasClass(class string, model *editorModels.NodeModel) bool {
109 | for _, modelClass := range model.Classes {
110 | if class == modelClass {
111 | return true
112 | }
113 | }
114 | return false
115 | }
116 |
--------------------------------------------------------------------------------
/controller/basicMovementController.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | type BasicMovementActor interface {
4 | Look(dx, dy float32)
5 | StartMovingUp()
6 | StartMovingDown()
7 | StartMovingRight()
8 | StartMovingLeft()
9 | StopMovingUp()
10 | StopMovingDown()
11 | StopMovingRight()
12 | StopMovingLeft()
13 | }
14 |
15 | func NewBasicMovementController(actor BasicMovementActor, useDrag bool) Controller {
16 | c := CreateController()
17 | var x, y float32 = 0.0, 0.0
18 | drag := false
19 | doLook := func(xpos, ypos float32) {
20 | if drag || !useDrag {
21 | actor.Look(xpos-x, ypos-y)
22 | }
23 | x, y = xpos, ypos
24 | }
25 | mouseClick := func() {
26 | drag = true
27 | }
28 | mouseRelease := func() {
29 | drag = false
30 | }
31 | c.BindAxisAction(doLook)
32 | c.BindMouseAction(mouseClick, MouseButtonLeft, Press)
33 | c.BindMouseAction(mouseRelease, MouseButtonLeft, Release)
34 |
35 | c.BindKeyAction(actor.StartMovingUp, KeyUp, Press)
36 | c.BindKeyAction(actor.StartMovingDown, KeyDown, Press)
37 | c.BindKeyAction(actor.StartMovingLeft, KeyLeft, Press)
38 | c.BindKeyAction(actor.StartMovingRight, KeyRight, Press)
39 | c.BindKeyAction(actor.StopMovingUp, KeyUp, Release)
40 | c.BindKeyAction(actor.StopMovingDown, KeyDown, Release)
41 | c.BindKeyAction(actor.StopMovingLeft, KeyLeft, Release)
42 | c.BindKeyAction(actor.StopMovingRight, KeyRight, Release)
43 |
44 | c.BindKeyAction(actor.StartMovingUp, KeyW, Press)
45 | c.BindKeyAction(actor.StartMovingDown, KeyS, Press)
46 | c.BindKeyAction(actor.StartMovingLeft, KeyA, Press)
47 | c.BindKeyAction(actor.StartMovingRight, KeyD, Press)
48 | c.BindKeyAction(actor.StopMovingUp, KeyW, Release)
49 | c.BindKeyAction(actor.StopMovingDown, KeyS, Release)
50 | c.BindKeyAction(actor.StopMovingLeft, KeyA, Release)
51 | c.BindKeyAction(actor.StopMovingRight, KeyD, Release)
52 | return c
53 | }
54 |
--------------------------------------------------------------------------------
/controller/controller.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | type Controller interface {
4 | SetKeyAction(function func(key Key, action Action))
5 | BindKeyAction(function func(), key Key, action Action)
6 | SetMouseAction(function func(button MouseButton, action Action))
7 | BindMouseAction(function func(), button MouseButton, action Action)
8 | BindAxisAction(function func(xpos, ypos float32))
9 | BindScrollAction(function func(xoffset, yoffset float32))
10 | }
11 |
--------------------------------------------------------------------------------
/controller/factory.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | var defaultConstructor func() Controller
4 |
5 | func CreateController() Controller {
6 | if defaultConstructor != nil {
7 | return defaultConstructor()
8 | }
9 | return nil
10 | }
11 |
12 | func SetDefaultConstructor(constructor func() Controller) {
13 | defaultConstructor = constructor
14 | }
15 |
--------------------------------------------------------------------------------
/controller/fpsController.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | type FPSActor interface {
4 | Look(dx, dy float32)
5 | StartMovingForward()
6 | StartMovingBackward()
7 | StartStrafingLeft()
8 | StartStrafingRight()
9 | StopMovingForward()
10 | StopMovingBackward()
11 | StopStrafingLeft()
12 | StopStrafingRight()
13 | Jump()
14 | StandUp()
15 | Crouch()
16 | Prone()
17 | StartSprinting()
18 | StopSprinting()
19 | }
20 |
21 | func NewFPSController(actor FPSActor) Controller {
22 | c := CreateController()
23 | var x, y float32 = 0.0, 0.0
24 | doLook := func(xpos, ypos float32) {
25 | actor.Look(xpos-x, ypos-y)
26 | x, y = xpos, ypos
27 | }
28 | c.BindAxisAction(doLook)
29 | c.BindKeyAction(actor.StartMovingForward, KeyW, Press)
30 | c.BindKeyAction(actor.StartMovingBackward, KeyS, Press)
31 | c.BindKeyAction(actor.StopMovingForward, KeyW, Release)
32 | c.BindKeyAction(actor.StopMovingBackward, KeyS, Release)
33 | c.BindKeyAction(actor.StartStrafingLeft, KeyA, Press)
34 | c.BindKeyAction(actor.StopStrafingLeft, KeyA, Release)
35 | c.BindKeyAction(actor.StartStrafingRight, KeyD, Press)
36 | c.BindKeyAction(actor.StopStrafingRight, KeyD, Release)
37 | c.BindKeyAction(actor.Jump, KeySpace, Press)
38 | c.BindKeyAction(actor.Crouch, KeyLeftControl, Press)
39 | c.BindKeyAction(actor.StandUp, KeyLeftControl, Release)
40 | c.BindKeyAction(actor.Prone, KeyZ, Press)
41 | c.BindKeyAction(actor.StartSprinting, KeyLeftShift, Press)
42 | c.BindKeyAction(actor.StopSprinting, KeyLeftShift, Release)
43 | return c
44 | }
45 |
--------------------------------------------------------------------------------
/editor/editorController.go:
--------------------------------------------------------------------------------
1 | package editor
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/controller"
6 | )
7 |
8 | const mouseSpeed = 0.01
9 |
10 | func NewEditorController(e *Editor) controller.Controller {
11 | c := controller.CreateController()
12 | var mouseDown bool
13 | var x, y float32 = 0.0, 0.0
14 | axisLock := mgl32.Vec3{1, 1, 1}
15 |
16 | doMouseMove := func(xpos, ypos float32) {
17 | if mouseDown {
18 | switch {
19 | case e.mouseMode == "scale":
20 | e.ScaleSelectedNodeModel(xpos-x, ypos-y, axisLock)
21 | case e.mouseMode == "translate":
22 | e.MoveSelectedNodeModel(xpos-x, ypos-y, axisLock)
23 | case e.mouseMode == "rotate":
24 | e.RotateSelectedNodeModel(xpos-x, ypos-y, axisLock)
25 | }
26 | }
27 | x, y = xpos, ypos
28 | }
29 | c.BindAxisAction(doMouseMove)
30 | c.BindMouseAction(func() { mouseDown = true }, controller.MouseButtonRight, controller.Press)
31 | c.BindMouseAction(func() { mouseDown = false }, controller.MouseButtonRight, controller.Release)
32 | c.BindKeyAction(func() { axisLock = mgl32.Vec3{1, 0, 0} }, controller.KeyX, controller.Press)
33 | c.BindKeyAction(func() { axisLock = mgl32.Vec3{0, 1, 0} }, controller.KeyY, controller.Press)
34 | c.BindKeyAction(func() { axisLock = mgl32.Vec3{0, 0, 1} }, controller.KeyZ, controller.Press)
35 |
36 | c.BindKeyAction(func() {
37 | e.mouseMode = "scale"
38 | axisLock = mgl32.Vec3{1, 1, 1}
39 | }, controller.KeyT, controller.Press)
40 | c.BindKeyAction(func() {
41 | e.mouseMode = "translate"
42 | axisLock = mgl32.Vec3{1, 0, 0}
43 | }, controller.KeyG, controller.Press)
44 | c.BindKeyAction(func() {
45 | e.mouseMode = "rotate"
46 | axisLock = mgl32.Vec3{0, 1, 0}
47 | }, controller.KeyR, controller.Press)
48 | return c
49 | }
50 |
51 | func (e *Editor) ScaleSelectedNodeModel(x, y float32, axisLock mgl32.Vec3) {
52 | selectedModel, _ := e.overviewMenu.getSelectedNode(e.currentMap.Root)
53 | if selectedModel != nil {
54 | selectedModel.Scale = selectedModel.Scale.Add(axisLock.Mul(x * mouseSpeed))
55 | }
56 | updateMap(e.currentMap.Root)
57 | }
58 |
59 | func (e *Editor) MoveSelectedNodeModel(x, y float32, axisLock mgl32.Vec3) {
60 | selectedModel, _ := e.overviewMenu.getSelectedNode(e.currentMap.Root)
61 | if selectedModel != nil {
62 | selectedModel.Translation = selectedModel.Translation.Add(axisLock.Mul(x * mouseSpeed))
63 | }
64 | updateMap(e.currentMap.Root)
65 | }
66 |
67 | func (e *Editor) RotateSelectedNodeModel(x, y float32, axisLock mgl32.Vec3) {
68 | selectedModel, _ := e.overviewMenu.getSelectedNode(e.currentMap.Root)
69 | if selectedModel != nil {
70 | selectedModel.Orientation = mgl32.QuatRotate(x*mouseSpeed, axisLock).Mul(selectedModel.Orientation).Normalize()
71 | }
72 | updateMap(e.currentMap.Root)
73 | }
74 |
--------------------------------------------------------------------------------
/editor/maps.go:
--------------------------------------------------------------------------------
1 | package editor
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "os"
9 |
10 | "github.com/Invictus321/invictus321-countdown"
11 | "github.com/walesey/go-engine/assets"
12 | "github.com/walesey/go-engine/editor/models"
13 | "github.com/walesey/go-engine/engine"
14 | "github.com/walesey/go-engine/renderer"
15 | )
16 |
17 | type MapLoadUpdate struct {
18 | node *renderer.Node
19 | geomsLoaded int
20 | }
21 |
22 | func (e *Editor) saveMap(filepath string) {
23 | data, err := json.MarshalIndent(e.currentMap, "", " ")
24 | if err != nil {
25 | log.Printf("Error Marshaling map file: %v\n", err)
26 | return
27 | }
28 |
29 | ioutil.WriteFile(filepath, data, os.ModePerm)
30 | }
31 |
32 | func (e *Editor) loadMap(path string) {
33 | e.currentMap = assets.LoadMap(path)
34 | e.refreshMap()
35 | e.overviewMenu.updateTree(e.currentMap)
36 | }
37 |
38 | func (e *Editor) refreshMap() {
39 | e.rootMapNode.RemoveAll(true)
40 |
41 | cd := countdown.Countdown{}
42 | cd.Start(countGeometries(e.currentMap.Root))
43 | e.openProgressBar()
44 | e.setProgressBar(0)
45 | e.setProgressTime("Loading Map...")
46 |
47 | mapLoadChan := loadMapToNode(e.currentMap.Root)
48 |
49 | var loader engine.Updatable
50 | loader = engine.UpdatableFunc(func(dt float64) {
51 | select {
52 | case mapLoadUpdate := <-mapLoadChan:
53 | if mapLoadUpdate.node == nil {
54 | cd.Count()
55 | e.setProgressBar(cd.PercentageComplete() / 5)
56 | e.setProgressTime(fmt.Sprintf("Loading Map... %v seconds remaining", cd.SecondsRemaining()))
57 | } else {
58 | e.rootMapNode.Add(mapLoadUpdate.node)
59 | e.gameEngine.RemoveUpdatable(loader)
60 | e.closeProgressBar()
61 | }
62 | default:
63 | }
64 | })
65 | e.gameEngine.AddUpdatable(loader)
66 | }
67 |
68 | func loadMapToNode(model *editorModels.NodeModel) chan MapLoadUpdate {
69 |
70 | out := make(chan MapLoadUpdate)
71 | geomsLoaded := 0
72 |
73 | var updateNode func(srcModel *editorModels.NodeModel, destNode *renderer.Node)
74 | updateNode = func(srcModel *editorModels.NodeModel, destNode *renderer.Node) {
75 | srcModel.SetNode(destNode)
76 | if srcModel.Geometry != nil {
77 | geometry, material, err := assets.ImportObjCached(*srcModel.Geometry)
78 | if err == nil {
79 | destNode.Material = material
80 | destNode.Add(geometry)
81 | }
82 | geomsLoaded++
83 | out <- MapLoadUpdate{nil, geomsLoaded}
84 | }
85 | destNode.SetScale(srcModel.Scale)
86 | destNode.SetTranslation(srcModel.Translation)
87 | destNode.SetOrientation(srcModel.Orientation)
88 | if srcModel.Reference != nil {
89 | if refModel, _ := findNodeById(*srcModel.Reference, model); refModel != nil {
90 | for _, childModel := range refModel.Children {
91 | refNode := childModel.GetNode()
92 | if refNode != nil {
93 | destNode.Add(refNode)
94 | }
95 | }
96 | }
97 | }
98 | for _, childModel := range srcModel.Children {
99 | newNode := renderer.NewNode()
100 | destNode.Add(newNode)
101 | updateNode(childModel, newNode)
102 | }
103 | }
104 |
105 | node := renderer.NewNode()
106 | go func() {
107 | updateNode(model, node)
108 | out <- MapLoadUpdate{node, geomsLoaded}
109 | }()
110 | return out
111 | }
112 |
113 | func updateMap(model *editorModels.NodeModel) {
114 | node := model.GetNode()
115 | if node != nil {
116 | node.SetScale(model.Scale)
117 | node.SetTranslation(model.Translation)
118 | node.SetOrientation(model.Orientation)
119 | }
120 | for _, childModel := range model.Children {
121 | updateMap(childModel)
122 | }
123 | }
124 |
125 | func countGeometries(nodeModel *editorModels.NodeModel) int {
126 | count := 0
127 | if nodeModel.Geometry != nil {
128 | count++
129 | }
130 | for _, child := range nodeModel.Children {
131 | count += countGeometries(child)
132 | }
133 | return count
134 | }
135 |
--------------------------------------------------------------------------------
/editor/models/map.go:
--------------------------------------------------------------------------------
1 | package editorModels
2 |
3 | type MapModel struct {
4 | Name string `json:"name"`
5 | Root *NodeModel `json:"root"`
6 | }
7 |
--------------------------------------------------------------------------------
/editor/models/node.go:
--------------------------------------------------------------------------------
1 | package editorModels
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/renderer"
6 | )
7 |
8 | type NodeModel struct {
9 | Id string `json:"id"`
10 | Classes []string `json:"classes"`
11 | Scale mgl32.Vec3 `json:"scale"`
12 | Translation mgl32.Vec3 `json:"translation"`
13 | Orientation mgl32.Quat `json:"orientation"`
14 | Geometry *string `json:"geometry"`
15 | Reference *string `json:"reference"`
16 | Children []*NodeModel `json:"children"`
17 | node *renderer.Node
18 | }
19 |
20 | func (n *NodeModel) GetNode() *renderer.Node {
21 | return n.node
22 | }
23 |
24 | func (n *NodeModel) SetNode(node *renderer.Node) {
25 | n.node = node
26 | }
27 |
28 | func (n *NodeModel) CopyShallow(nameGenerator func(name string) string) *NodeModel {
29 | copiedNode := &NodeModel{
30 | Id: nameGenerator(n.Id),
31 | Classes: n.Classes,
32 | Scale: n.Scale,
33 | Translation: n.Translation,
34 | Orientation: n.Orientation,
35 | Children: make([]*NodeModel, len(n.Children)),
36 | }
37 | if n.Geometry != nil {
38 | copiedNode.Geometry = n.Geometry
39 | }
40 | if n.Reference != nil {
41 | ref := *n.Reference
42 | copiedNode.Reference = &ref
43 | }
44 | return copiedNode
45 | }
46 |
47 | func (n *NodeModel) Copy(nameGenerator func(name string) string) *NodeModel {
48 | copiedNode := n.CopyShallow(nameGenerator)
49 | for i, child := range n.Children {
50 | copiedNode.Children[i] = child.Copy(nameGenerator)
51 | }
52 | return copiedNode
53 | }
54 |
55 | func NewNodeModel(id string) *NodeModel {
56 | return &NodeModel{
57 | Id: id,
58 | Scale: mgl32.Vec3{1, 1, 1},
59 | Translation: mgl32.Vec3{},
60 | Orientation: mgl32.QuatIdent(),
61 | Children: make([]*NodeModel, 0),
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/editor/nodeEditor.go:
--------------------------------------------------------------------------------
1 | package editor
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "github.com/go-gl/mathgl/mgl32"
8 | "github.com/walesey/go-engine/editor/models"
9 | "github.com/walesey/go-engine/glfwController"
10 | "github.com/walesey/go-engine/ui"
11 | )
12 |
13 | func (e *Editor) closeNodeEditor() {
14 | if e.fileBrowserOpen {
15 | e.gameEngine.RemoveSpatial(e.fileBrowser.window, false)
16 | e.fileBrowserOpen = false
17 | }
18 | }
19 |
20 | func (e *Editor) openNodeEditor(node *editorModels.NodeModel, callback func()) {
21 | window, container, _ := e.defaultWindow()
22 | window.SetTranslation(mgl32.Vec3{100, 100, 1})
23 | window.SetScale(mgl32.Vec3{500, 0, 1})
24 | uiController := ui.NewUiController(window).(glfwController.Controller)
25 |
26 | e.uiAssets.AddCallback("nodeEditorOk", func(element ui.Element, args ...interface{}) {
27 | if len(args) >= 2 && !args[1].(bool) { // not on release
28 | newId := window.TextElementById("name").GetText()
29 | if check, _ := findNodeById(newId, e.currentMap.Root); check != nil {
30 | return // id already taken
31 | }
32 | node.Id = newId
33 | node.Classes = []string{}
34 | for i := 1; i <= 3; i++ {
35 | className := window.TextElementById(fmt.Sprintf("class%v", i)).GetText()
36 | if len(className) > 0 {
37 | node.Classes = append(node.Classes, className)
38 | }
39 | }
40 | e.gameEngine.RemoveSpatial(window, true)
41 | e.controllerManager.RemoveController(uiController)
42 | callback()
43 | }
44 | })
45 |
46 | e.uiAssets.AddCallback("nodeEditorCancel", func(element ui.Element, args ...interface{}) {
47 | if len(args) >= 2 && !args[1].(bool) { // not on release
48 | e.gameEngine.RemoveSpatial(window, true)
49 | e.controllerManager.RemoveController(uiController)
50 | }
51 | })
52 |
53 | e.controllerManager.AddController(uiController)
54 | window.Tabs, _ = ui.LoadHTML(container, strings.NewReader(nodeEditMenuHtml), strings.NewReader(globalCss), e.uiAssets)
55 |
56 | e.gameEngine.AddOrtho(window)
57 |
58 | window.TextElementById("name").SetText(node.Id)
59 | for i, class := range node.Classes {
60 | if i < 3 {
61 | window.TextElementById(fmt.Sprintf("class%v", i+1)).SetText(class)
62 | }
63 | }
64 | window.Render()
65 | }
66 |
--------------------------------------------------------------------------------
/editor/progressBar.go:
--------------------------------------------------------------------------------
1 | package editor
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "github.com/go-gl/mathgl/mgl32"
8 | "github.com/walesey/go-engine/glfwController"
9 | "github.com/walesey/go-engine/ui"
10 | )
11 |
12 | func (e *Editor) closeProgressBar() {
13 | e.gameEngine.RemoveSpatial(e.progressBar, false)
14 | }
15 |
16 | func (e *Editor) openProgressBar() {
17 | if e.progressBar == nil {
18 | window := ui.NewWindow()
19 | window.SetTranslation(mgl32.Vec3{500, 0, 0})
20 | window.SetScale(mgl32.Vec3{330, 0, 1})
21 |
22 | container := ui.NewContainer()
23 | container.SetBackgroundColor(200, 200, 200, 255)
24 | window.SetElement(container)
25 |
26 | e.controllerManager.AddController(ui.NewUiController(window).(glfwController.Controller))
27 | ui.LoadHTML(container, strings.NewReader(progressBarHtml), strings.NewReader(globalCss), e.uiAssets)
28 | window.Render()
29 |
30 | e.progressBar = window
31 | }
32 | e.gameEngine.AddOrtho(e.progressBar)
33 | }
34 |
35 | func (e *Editor) setProgressBar(progress int) {
36 | for i := 1; i <= 20; i++ {
37 | container, ok := e.progressBar.ElementById(fmt.Sprintf("progress%v", i)).(*ui.Container)
38 | if ok {
39 | if i > progress {
40 | container.SetBackgroundColor(0, 0, 0, 0)
41 | } else {
42 | container.SetBackgroundColor(0, 255, 0, 255)
43 | }
44 | }
45 | }
46 | }
47 |
48 | func (e *Editor) setProgressTime(message string) {
49 | container, ok := e.progressBar.ElementById("progressBarMessage").(*ui.Container)
50 | if ok {
51 | container.RemoveAllChildren()
52 | html := fmt.Sprintf("
%v
", message)
53 | css := "p { font-size: 8px; }"
54 | ui.LoadHTML(container, strings.NewReader(html), strings.NewReader(css), e.uiAssets)
55 | e.progressBar.Render()
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/editor/ui.go:
--------------------------------------------------------------------------------
1 | package editor
2 |
3 | import (
4 | "bytes"
5 | "os"
6 | "strings"
7 |
8 | "github.com/go-gl/mathgl/mgl32"
9 | "github.com/walesey/go-engine/assets"
10 | "github.com/walesey/go-engine/controller"
11 | "github.com/walesey/go-engine/glfwController"
12 | "github.com/walesey/go-engine/ui"
13 | "github.com/walesey/go-engine/util"
14 | )
15 |
16 | func (e *Editor) setupUI() {
17 |
18 | //images
19 | loadImageAsset("file", FileIconData, e.uiAssets)
20 | loadImageAsset("copy", CopyIconData, e.uiAssets)
21 | loadImageAsset("reference", LinkIconData, e.uiAssets)
22 | loadImageAsset("folderOpen", FolderOpenData, e.uiAssets)
23 | loadImageAsset("folderClosed", FolderClosedData, e.uiAssets)
24 | loadImageAsset("planetOpen", PlanetOpenData, e.uiAssets)
25 | loadImageAsset("planetClosed", PlanetClosedData, e.uiAssets)
26 | loadImageAsset("trash", TrashIconData, e.uiAssets)
27 | loadImageAsset("geometry", GeometryIconData, e.uiAssets)
28 | loadImageAsset("scale", ScaleIconData, e.uiAssets)
29 | loadImageAsset("translate", TranslateIconData, e.uiAssets)
30 | loadImageAsset("rotate", RotateIconData, e.uiAssets)
31 | loadImageAsset("reset", ResetIconData, e.uiAssets)
32 | loadImageAsset("cog", CogIconData, e.uiAssets)
33 |
34 | // callbacks used to highlight text active text fields
35 | e.uiAssets.AddCallback("inputfocus", func(element ui.Element, args ...interface{}) {
36 | container, ok := element.(*ui.Container)
37 | if ok {
38 | container.SetBackgroundColor(255, 255, 50, 255)
39 | }
40 | })
41 | e.uiAssets.AddCallback("inputblur", func(element ui.Element, args ...interface{}) {
42 | container, ok := element.(*ui.Container)
43 | if ok {
44 | container.SetBackgroundColor(255, 255, 255, 255)
45 | }
46 | })
47 |
48 | e.initOverviewMenu()
49 | e.gameEngine.InitFpsDial()
50 |
51 | e.customController.BindKeyAction(func() {
52 | if e.mainMenuOpen {
53 | e.mainMenuOpen = false
54 | e.closeMainMenu()
55 | } else {
56 | e.mainMenuOpen = true
57 | e.openMainMenu()
58 | }
59 | }, controller.KeyEscape, controller.Press)
60 | }
61 |
62 | func loadImageAsset(key, data string, uiAssets ui.HtmlAssets) {
63 | img, _ := assets.DecodeImage(bytes.NewBuffer(util.Base64ToBytes(data)))
64 | uiAssets.AddImage(key, img)
65 | }
66 |
67 | func (e *Editor) closeMainMenu() {
68 | e.gameEngine.RemoveSpatial(e.mainMenu, false)
69 | e.controllerManager.RemoveController(e.mainMenuController)
70 | }
71 |
72 | func (e *Editor) openMainMenu() {
73 | if e.mainMenu == nil {
74 |
75 | e.uiAssets.AddCallback("open", func(element ui.Element, args ...interface{}) {
76 | if len(args) >= 2 && !args[1].(bool) { // not on release
77 | e.openFileBrowser("Open", func(filePath string) {
78 | e.loadMap(filePath)
79 | e.closeFileBrowser()
80 | }, ".json")
81 | }
82 | })
83 | e.uiAssets.AddCallback("save", func(element ui.Element, args ...interface{}) {
84 | if len(args) >= 2 && !args[1].(bool) { // not on release
85 | e.openFileBrowser("Save", func(filePath string) {
86 | e.saveMap(filePath)
87 | e.closeFileBrowser()
88 | }, ".json")
89 | }
90 | })
91 | e.uiAssets.AddCallback("exit", func(element ui.Element, args ...interface{}) {
92 | os.Exit(0)
93 | })
94 |
95 | window, container, _ := e.defaultWindow()
96 | window.SetTranslation(mgl32.Vec3{200, 200, 1})
97 | window.SetScale(mgl32.Vec3{400, 0, 1})
98 |
99 | ui.LoadHTML(container, strings.NewReader(mainMenuHtml), strings.NewReader(globalCss), e.uiAssets)
100 | window.Render()
101 |
102 | e.mainMenuController = ui.NewUiController(window).(glfwController.Controller)
103 | e.mainMenu = window
104 | }
105 | e.gameEngine.AddOrtho(e.mainMenu)
106 | e.controllerManager.AddController(e.mainMenuController)
107 | }
108 |
109 | func (e *Editor) defaultWindow() (window *ui.Window, container *ui.Container, tab *ui.Container) {
110 | window = ui.NewWindow()
111 |
112 | tab = ui.NewContainer()
113 | tab.SetBackgroundColor(70, 70, 70, 255)
114 | tab.SetHeight(40)
115 |
116 | mainContainer := ui.NewContainer()
117 | window.SetElement(mainContainer)
118 | container = ui.NewContainer()
119 | container.SetBackgroundColor(200, 200, 200, 255)
120 | mainContainer.AddChildren(tab, container)
121 | ui.ClickAndDragWindow(window, tab.Hitbox, e.customController)
122 |
123 | return
124 | }
125 |
--------------------------------------------------------------------------------
/effects/particleGroup.go:
--------------------------------------------------------------------------------
1 | package effects
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/renderer"
6 | )
7 |
8 | type ParticleGroup struct {
9 | Node *renderer.Node
10 | camera *renderer.Camera
11 | particles []*ParticleSystem
12 | }
13 |
14 | func (pg *ParticleGroup) Disable(disable bool) {
15 | for _, particle := range pg.particles {
16 | particle.DisableSpawning = disable
17 | }
18 | }
19 |
20 | func (pg *ParticleGroup) Update(dt float64) {
21 | for _, particle := range pg.particles {
22 | if pg.camera != nil {
23 | particle.SetCameraLocation(pg.camera.Translation)
24 | }
25 | particle.Update(dt)
26 | }
27 | }
28 |
29 | func (pg *ParticleGroup) SetTranslation(translation mgl32.Vec3) {
30 | for _, particle := range pg.particles {
31 | particle.Location = translation
32 | }
33 | }
34 |
35 | func (pg *ParticleGroup) Draw(renderer renderer.Renderer, transform mgl32.Mat4) {
36 | pg.Node.Draw(renderer, transform)
37 | }
38 |
39 | func (pg *ParticleGroup) Destroy(renderer renderer.Renderer) {
40 | pg.Node.Destroy(renderer)
41 | }
42 |
43 | func (pg *ParticleGroup) Center() mgl32.Vec3 {
44 | return pg.Node.Center()
45 | }
46 |
47 | func (pg *ParticleGroup) SetParent(parent *renderer.Node) {
48 | pg.Node.SetParent(parent)
49 | }
50 |
51 | func (pg *ParticleGroup) Optimize(geometry *renderer.Geometry, transform mgl32.Mat4) {
52 | pg.Node.Optimize(geometry, transform)
53 | }
54 |
55 | func (pg *ParticleGroup) BoundingRadius() float32 {
56 | return pg.Node.BoundingRadius()
57 | }
58 |
59 | func (pg *ParticleGroup) OrthoOrder() int {
60 | return pg.Node.OrthoOrder()
61 | }
62 |
63 | func NewParticleGroup(camera *renderer.Camera, particles ...*ParticleSystem) *ParticleGroup {
64 | node := renderer.NewNode()
65 | for _, particle := range particles {
66 | node.Add(particle)
67 | }
68 | return &ParticleGroup{
69 | Node: node,
70 | camera: camera,
71 | particles: particles,
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/effects/particleSystem_test.go:
--------------------------------------------------------------------------------
1 | package effects
2 |
3 | import (
4 | "image/color"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestLerpColor(t *testing.T) {
11 | c := lerpColor(color.NRGBA{123, 10, 55, 50}, color.NRGBA{223, 60, 20, 240}, 0.0)
12 | assert.EqualValues(t, color.NRGBA{123, 10, 55, 50}, c, "Lerp Color")
13 |
14 | c = lerpColor(color.NRGBA{123, 10, 55, 50}, color.NRGBA{223, 60, 20, 240}, 1.0)
15 | assert.EqualValues(t, color.NRGBA{223, 60, 20, 240}, c, "Lerp Color")
16 |
17 | c = lerpColor(color.NRGBA{120, 10, 50, 50}, color.NRGBA{220, 60, 20, 240}, 0.5)
18 | assert.EqualValues(t, color.NRGBA{170, 35, 35, 145}, c, "Lerp Color")
19 | }
20 |
--------------------------------------------------------------------------------
/effects/sprite.go:
--------------------------------------------------------------------------------
1 | package effects
2 |
3 | import (
4 | "image/color"
5 |
6 | "github.com/go-gl/mathgl/mgl32"
7 | "github.com/walesey/go-engine/renderer"
8 | "github.com/walesey/go-engine/util"
9 | )
10 |
11 | type Sprite struct {
12 | Node *renderer.Node
13 | geometry *renderer.Geometry
14 | Rotation float32
15 | FaceCamera bool
16 | cameraPosition mgl32.Vec3
17 | frame, totalFrames, framesX, framesY int
18 | }
19 |
20 | func CreateSprite(totalFrames, framesX, framesY int, material *renderer.Material) *Sprite {
21 | sprite := Sprite{
22 | frame: 0,
23 | FaceCamera: true,
24 | totalFrames: totalFrames,
25 | framesX: framesX,
26 | framesY: framesY,
27 | }
28 | geometry := renderer.CreateBox(1, 1)
29 | sprite.geometry = geometry
30 | sprite.Node = renderer.NewNode()
31 | sprite.Node.Material = material
32 | sprite.Node.Add(sprite.geometry)
33 | return &sprite
34 | }
35 |
36 | func BoxFlipbook(geometry *renderer.Geometry, frame, framesX, framesY int) {
37 | frameSizeX := 1.0 / float32(framesX)
38 | frameSizeY := 1.0 / float32(framesY)
39 | indexX := float32(frame % framesX)
40 | indexY := float32(framesY - (frame / framesY) - 1)
41 | u1, u2 := frameSizeX*indexX, frameSizeX*(indexX+1.0)
42 | v1, v2 := frameSizeY*indexY, frameSizeY*(indexY+1.0)
43 | geometry.SetUVs(u1, v1, u2, v1, u2, v2, u1, v2)
44 | }
45 |
46 | func (sprite *Sprite) Draw(r renderer.Renderer, transform mgl32.Mat4) {
47 | sprite.Node.Draw(r, transform)
48 | }
49 |
50 | func (sprite *Sprite) Destroy(renderer renderer.Renderer) {
51 | sprite.Node.Destroy(renderer)
52 | }
53 |
54 | func (sprite *Sprite) Center() mgl32.Vec3 {
55 | return sprite.Node.Center()
56 | }
57 |
58 | func (sprite *Sprite) SetParent(parent *renderer.Node) {
59 | sprite.Node.SetParent(parent)
60 | }
61 |
62 | func (sprite *Sprite) NextFrame() {
63 | sprite.frame = sprite.frame + 1
64 | if sprite.frame >= sprite.totalFrames {
65 | sprite.frame = 0
66 | }
67 | BoxFlipbook(sprite.geometry, sprite.frame, sprite.framesX, sprite.framesY)
68 | }
69 |
70 | func (sprite *Sprite) SetColor(color color.NRGBA) {
71 | sprite.geometry.SetColor(color)
72 | }
73 |
74 | func (sprite *Sprite) Optimize(geometry *renderer.Geometry, transform mgl32.Mat4) {
75 | sprite.geometry.Optimize(geometry, transform)
76 | }
77 |
78 | func (sprite *Sprite) BoundingRadius() float32 {
79 | return sprite.Node.BoundingRadius()
80 | }
81 |
82 | func (sprite *Sprite) OrthoOrder() int {
83 | return sprite.Node.OrthoOrder()
84 | }
85 |
86 | func (sprite *Sprite) SetTranslation(translation mgl32.Vec3) {
87 | sprite.Node.SetTranslation(translation)
88 | }
89 |
90 | func (sprite *Sprite) SetScale(scale mgl32.Vec3) {
91 | sprite.Node.SetScale(scale)
92 | }
93 |
94 | func (sprite *Sprite) SetOrientation(orientation mgl32.Quat) {
95 | sprite.Node.SetOrientation(orientation)
96 | }
97 |
98 | func (sprite *Sprite) SetCameraLocation(cameraLocation mgl32.Vec3) {
99 | sprite.cameraPosition = cameraLocation
100 | }
101 |
102 | func (sprite *Sprite) Update(dt float64) {
103 | if sprite.FaceCamera {
104 | orientation := util.FacingOrientation(sprite.Rotation, sprite.cameraPosition.Sub(sprite.Node.Translation), mgl32.Vec3{0, 0, 1}, mgl32.Vec3{-1, 0, 0})
105 | sprite.Node.SetOrientation(orientation)
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/effects/util.go:
--------------------------------------------------------------------------------
1 | package effects
2 |
3 | import (
4 | "github.com/walesey/go-engine/engine"
5 | "github.com/walesey/go-engine/renderer"
6 | )
7 |
8 | type TimedParticleGroup struct {
9 | GameEngine engine.Engine
10 | TargetNode *renderer.Node
11 | Particles *ParticleGroup
12 | Life, Cleanup float64
13 | }
14 |
15 | func TriggerTimedParticleGroup(tpg TimedParticleGroup) {
16 | tpg.TargetNode.Add(tpg.Particles)
17 | tpg.GameEngine.AddUpdatable(tpg.Particles)
18 | particleLife := tpg.Life
19 |
20 | var updater engine.Updatable
21 | updater = engine.UpdatableFunc(func(dt float64) {
22 | particleLife -= dt
23 | if particleLife < 0 {
24 | tpg.Particles.Disable(true)
25 | }
26 | if particleLife < -tpg.Cleanup {
27 | tpg.TargetNode.Remove(tpg.Particles, true)
28 | tpg.GameEngine.RemoveUpdatable(tpg.Particles)
29 | tpg.GameEngine.RemoveUpdatable(updater)
30 | }
31 | })
32 | tpg.GameEngine.AddUpdatable(updater)
33 | }
34 |
--------------------------------------------------------------------------------
/emitter/emitter.go:
--------------------------------------------------------------------------------
1 | package emitter
2 |
3 | import "sync"
4 |
5 | type Event interface{}
6 |
7 | type EventChan <-chan Event
8 |
9 | type EventEmitter interface {
10 | On(topic string, handlers ...func(Event)) EventChan
11 | Off(topic string, channels ...EventChan)
12 | Listeners(topic string) []EventChan
13 | Emit(topic string, event Event)
14 | Do(topic string, event Event)
15 | Close()
16 | FlushAll()
17 | Flush(topic string)
18 | }
19 |
20 | type listener struct {
21 | handlers []func(Event)
22 | ch chan Event
23 | }
24 |
25 | type listeners []listener
26 |
27 | type Emitter struct {
28 | topics map[string]listeners
29 | channelSize int
30 | mux *sync.Mutex
31 | }
32 |
33 | func New(channelSize int) *Emitter {
34 | return &Emitter{
35 | topics: make(map[string]listeners),
36 | channelSize: channelSize,
37 | mux: &sync.Mutex{},
38 | }
39 | }
40 |
41 | // On - creates a new topic listener with optional handlers and returns the
42 | func (e *Emitter) On(topic string, handlers ...func(Event)) EventChan {
43 | l := listener{
44 | handlers: handlers,
45 | ch: make(chan Event, e.channelSize),
46 | }
47 | if topicListeners, ok := e.getListeners(topic); ok {
48 | e.setListeners(topic, append(topicListeners, l))
49 | } else {
50 | e.setListeners(topic, listeners{l})
51 | }
52 | return l.ch
53 | }
54 |
55 | func (e *Emitter) Off(topic string, channels ...EventChan) {
56 | if topicListeners, ok := e.getListeners(topic); ok {
57 | if len(channels) > 0 {
58 | for _, ch := range channels {
59 | for i := len(topicListeners) - 1; i >= 0; i-- {
60 | if topicListeners[i].ch == ch {
61 | close(topicListeners[i].ch)
62 | topicListeners = append(topicListeners[:i], topicListeners[i+1:]...)
63 | }
64 | }
65 | }
66 | e.setListeners(topic, topicListeners)
67 | } else {
68 | for _, l := range topicListeners {
69 | close(l.ch)
70 | }
71 | e.deleteListeners(topic)
72 | }
73 | }
74 | }
75 |
76 | func (e *Emitter) Listeners(topic string) []EventChan {
77 | if topicListeners, ok := e.getListeners(topic); ok {
78 | listeners := make([]EventChan, len(topicListeners))
79 | for i, l := range topicListeners {
80 | listeners[i] = l.ch
81 | }
82 | return listeners
83 | }
84 | return []EventChan{}
85 | }
86 |
87 | // Emit - writes an event to the given topic
88 | func (e *Emitter) Emit(topic string, event Event) {
89 | e.mux.Lock()
90 | defer e.mux.Unlock()
91 | if topicListeners, ok := e.topics[topic]; ok {
92 | for _, l := range topicListeners {
93 | l.ch <- event
94 | }
95 | }
96 | }
97 |
98 | // Do - writes an event to the given topic, then flushes the topic
99 | func (e *Emitter) Do(topic string, event Event) {
100 | e.Emit(topic, event)
101 | e.Flush(topic)
102 | }
103 |
104 | // Close - closes all channels and removes all topics
105 | func (e *Emitter) Close() {
106 | e.mux.Lock()
107 | defer e.mux.Unlock()
108 | for _, topicListeners := range e.topics {
109 | for _, l := range topicListeners {
110 | close(l.ch)
111 | }
112 | }
113 | e.topics = make(map[string]listeners)
114 | }
115 |
116 | // FlushAll - flushes all events from all channels and calls handlers for them
117 | func (e *Emitter) FlushAll() {
118 | e.mux.Lock()
119 | defer e.mux.Unlock()
120 | for topic := range e.topics {
121 | e.mux.Unlock()
122 | e.Flush(topic)
123 | e.mux.Lock()
124 | }
125 | }
126 |
127 | // Flush - flushes all events from the topic channels and calls handlers for them
128 | func (e *Emitter) Flush(topic string) {
129 | topicListeners, ok := e.getListeners(topic)
130 | if ok {
131 | for _, l := range topicListeners {
132 | l.flush()
133 | }
134 | }
135 | }
136 |
137 | func (l listener) flush() {
138 | Loop:
139 | for {
140 | select {
141 | case event := <-l.ch:
142 | if event == nil {
143 | break Loop
144 | }
145 | for _, h := range l.handlers {
146 | h(event)
147 | }
148 | default:
149 | break Loop
150 | }
151 | }
152 | }
153 |
154 | func (e *Emitter) getListeners(topic string) (l listeners, ok bool) {
155 | e.mux.Lock()
156 | defer e.mux.Unlock()
157 | l, ok = e.topics[topic]
158 | return
159 | }
160 |
161 | func (e *Emitter) setListeners(topic string, l listeners) {
162 | e.mux.Lock()
163 | defer e.mux.Unlock()
164 | e.topics[topic] = l
165 | }
166 |
167 | func (e *Emitter) deleteListeners(topic string) {
168 | e.mux.Lock()
169 | defer e.mux.Unlock()
170 | delete(e.topics, topic)
171 | }
172 |
--------------------------------------------------------------------------------
/emitter/emitter_test.go:
--------------------------------------------------------------------------------
1 | package emitter
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | type eventChecker struct {
11 | event Event
12 | }
13 |
14 | func (e *eventChecker) handler(event Event) {
15 | e.event = event
16 | }
17 |
18 | func TestSyncronousEvents(t *testing.T) {
19 | e := New(5)
20 | helloEvent := "helloEvent"
21 | otherEvent := "otherEvent"
22 | checker := &eventChecker{}
23 | otherChecker := &eventChecker{}
24 | e.On("hello", checker.handler)
25 | e.On("other", otherChecker.handler)
26 |
27 | e.Emit("hello", helloEvent)
28 | e.FlushAll()
29 | assert.EqualValues(t, checker.event, helloEvent)
30 | assert.NotEqual(t, otherChecker.event, helloEvent)
31 |
32 | e.Emit("other", otherEvent)
33 | e.FlushAll()
34 | assert.NotEqual(t, checker.event, otherEvent)
35 | assert.EqualValues(t, otherChecker.event, otherEvent)
36 | }
37 |
38 | func TestASyncronousEvents(t *testing.T) {
39 | e := New(5)
40 | helloEvent := "helloEvent"
41 | otherEvent := "otherEvent"
42 | helloChan := e.On("hello")
43 | otherChan := e.On("other")
44 |
45 | go e.Emit("hello", helloEvent)
46 | result := <-helloChan
47 | assert.EqualValues(t, result, helloEvent)
48 |
49 | go e.Emit("other", otherEvent)
50 | result = <-otherChan
51 | assert.EqualValues(t, result, otherEvent)
52 | }
53 |
54 | func TestOffCleanup(t *testing.T) {
55 | e := New(5)
56 | helloChan := e.On("hello")
57 | helloChan2 := e.On("hello")
58 | assert.EqualValues(t, len(e.Listeners("hello")), 2)
59 |
60 | e.Off("hello", helloChan)
61 | assert.EqualValues(t, len(e.Listeners("hello")), 1)
62 | assert.EqualValues(t, e.Listeners("hello")[0], helloChan2)
63 |
64 | helloChan3 := e.On("hello")
65 | e.Off("hello", helloChan3)
66 | assert.EqualValues(t, len(e.Listeners("hello")), 1)
67 | assert.EqualValues(t, e.Listeners("hello")[0], helloChan2)
68 | }
69 |
70 | func TestOffCleanupNested(t *testing.T) {
71 | e := New(5)
72 | var helloChan EventChan
73 | helloChan = e.On("hello", func(event Event) {
74 | fmt.Println("hellooo")
75 | e.Off("hello", helloChan)
76 | })
77 | assert.EqualValues(t, 1, len(e.Listeners("hello")))
78 |
79 | e.Emit("hello", "event")
80 | e.FlushAll()
81 | assert.EqualValues(t, 0, len(e.Listeners("hello")))
82 | }
83 |
--------------------------------------------------------------------------------
/engine/updatable.go:
--------------------------------------------------------------------------------
1 | package engine
2 |
3 | type Updatable interface {
4 | Update(dt float64)
5 | }
6 |
7 | type updatableImpl struct {
8 | updateFunc func(dt float64)
9 | }
10 |
11 | func (updatable *updatableImpl) Update(dt float64) {
12 | updatable.updateFunc(dt)
13 | }
14 |
15 | func UpdatableFunc(update func(dt float64)) Updatable {
16 | return &updatableImpl{update}
17 | }
18 |
19 | func UpdatableFanIn(updatables ...Updatable) Updatable {
20 | return UpdatableFunc(func(dt float64) {
21 | for _, u := range updatables {
22 | u.Update(dt)
23 | }
24 | })
25 | }
26 |
27 | type UpdatableStore struct {
28 | updatables []Updatable
29 | }
30 |
31 | func NewUpdatableStore() *UpdatableStore {
32 | return &UpdatableStore{
33 | updatables: make([]Updatable, 0),
34 | }
35 | }
36 |
37 | func (store *UpdatableStore) UpdateAll(dt float64) {
38 | for _, updatable := range store.updatables {
39 | if updatable != nil {
40 | updatable.Update(dt)
41 | }
42 | }
43 | }
44 |
45 | func (store *UpdatableStore) Add(updatable Updatable) {
46 | store.updatables = append(store.updatables, updatable)
47 | }
48 |
49 | func (store *UpdatableStore) Remove(updatable Updatable) {
50 | for i, u := range store.updatables {
51 | if updatable == u {
52 | store.updatables[i] = store.updatables[len(store.updatables)-1]
53 | store.updatables[len(store.updatables)-1] = nil
54 | store.updatables = store.updatables[:len(store.updatables)-1]
55 | break
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/engine/util.go:
--------------------------------------------------------------------------------
1 | package engine
2 |
3 | func Timeout(e Engine, time float64, fn func()) Updatable {
4 | timer := time
5 | var updater Updatable
6 | updater = UpdatableFunc(func(dt float64) {
7 | if timer -= dt; timer <= 0 {
8 | fn()
9 | e.RemoveUpdatable(updater)
10 | }
11 | })
12 | e.AddUpdatable(updater)
13 | return updater
14 | }
15 |
16 | func Interval(e Engine, time float64, fn func()) Updatable {
17 | timer := 0.0
18 | var updater Updatable
19 | updater = UpdatableFunc(func(dt float64) {
20 | for timer += dt; timer >= time; timer -= time {
21 | fn()
22 | }
23 | })
24 | e.AddUpdatable(updater)
25 | return updater
26 | }
27 |
--------------------------------------------------------------------------------
/examples/simple/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "go/build"
5 | "image/color"
6 | "os"
7 | "runtime"
8 |
9 | "github.com/go-gl/mathgl/mgl32"
10 | "github.com/walesey/go-engine/actor"
11 | "github.com/walesey/go-engine/assets"
12 | "github.com/walesey/go-engine/controller"
13 | "github.com/walesey/go-engine/engine"
14 | "github.com/walesey/go-engine/glfwController"
15 | "github.com/walesey/go-engine/opengl"
16 | "github.com/walesey/go-engine/renderer"
17 | )
18 |
19 | func init() {
20 | // Use all cpu cores
21 | runtime.GOMAXPROCS(runtime.NumCPU())
22 | //Set default glfw controller
23 | controller.SetDefaultConstructor(glfwController.NewActionMap)
24 | // set working dir to access assets
25 | p, _ := build.Import("github.com/walesey/go-engine", "", build.FindOnly)
26 | os.Chdir(p.Dir)
27 | }
28 |
29 | //
30 | func main() {
31 |
32 | glRenderer := opengl.NewOpenglRenderer("Simple", 800, 800, false)
33 | gameEngine := engine.NewEngine(glRenderer)
34 | gameEngine.InitFpsDial()
35 |
36 | gameEngine.Start(func() {
37 |
38 | if shader, err := assets.ImportShader("shaders/build/basic.vert", "shaders/build/basic.frag"); err == nil {
39 | gameEngine.DefaultShader(shader)
40 | }
41 |
42 | // sky cube
43 | skyImg, err := assets.ImportImage("resources/cubemap.png")
44 | if err == nil {
45 | geom := renderer.CreateSkyBox()
46 | geom.Transform(mgl32.Scale3D(10000, 10000, 10000))
47 | skyNode := renderer.NewNode()
48 | skyNode.SetOrientation(mgl32.QuatRotate(1.57, mgl32.Vec3{0, 1, 0}))
49 | skyNode.Material = renderer.NewMaterial(renderer.NewTexture("diffuseMap", skyImg, false))
50 | skyNode.RendererParams = renderer.NewRendererParams()
51 | skyNode.RendererParams.CullBackface = false
52 | skyNode.RendererParams.Unlit = true
53 | skyNode.Add(geom)
54 | gameEngine.AddSpatial(skyNode)
55 | }
56 |
57 | // Add some light to the scene
58 | ambientLight := renderer.NewLight(renderer.AMBIENT)
59 | ambientLight.Color = [3]float32{0.3, 0.3, 0.3}
60 | gameEngine.AddLight(ambientLight)
61 |
62 | // Create a red box geometry, attach to a node, add the node to the scenegraph
63 | boxGeometry := renderer.CreateBox(10, 10)
64 | boxGeometry.SetColor(color.NRGBA{254, 0, 0, 254})
65 | boxNode := renderer.NewNode()
66 | boxNode.RendererParams = renderer.NewRendererParams()
67 | boxNode.RendererParams.CullBackface = false
68 | boxNode.Material = renderer.NewMaterial()
69 | boxNode.SetTranslation(mgl32.Vec3{30, 0})
70 | boxNode.Add(boxGeometry)
71 | gameEngine.AddSpatial(boxNode)
72 |
73 | // make the box spin
74 | var angle float64
75 | gameEngine.AddUpdatable(engine.UpdatableFunc(func(dt float64) {
76 | angle += dt
77 | q := mgl32.QuatRotate(float32(angle), mgl32.Vec3{0, 1, 0})
78 | boxNode.SetOrientation(q)
79 | }))
80 |
81 | // input/controller manager
82 | controllerManager := glfwController.NewControllerManager(glRenderer.Window)
83 |
84 | // camera + wasd controls
85 | camera := gameEngine.Camera()
86 | freeMoveActor := actor.NewFreeMoveActor(camera)
87 | freeMoveActor.Location = mgl32.Vec3{}
88 | mainController := controller.NewBasicMovementController(freeMoveActor, false)
89 | controllerManager.AddController(mainController.(glfwController.Controller))
90 | gameEngine.AddUpdatable(freeMoveActor)
91 |
92 | //lock the cursor
93 | glRenderer.LockCursor(true)
94 |
95 | // custom key bindings
96 | customController := controller.CreateController()
97 | controllerManager.AddController(customController.(glfwController.Controller))
98 |
99 | // close window and exit on escape
100 | customController.BindKeyAction(func() {
101 | glRenderer.Window.SetShouldClose(true)
102 | }, controller.KeyEscape, controller.Press)
103 | })
104 | }
105 |
--------------------------------------------------------------------------------
/glfwController/controller.go:
--------------------------------------------------------------------------------
1 | package glfwController
2 |
3 | import (
4 | "github.com/go-gl/glfw/v3.1/glfw"
5 | "github.com/walesey/go-engine/controller"
6 | )
7 |
8 | type Controller interface {
9 | controller.Controller
10 | KeyCallback(window *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey)
11 | MouseButtonCallback(window *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey)
12 | CursorPosCallback(window *glfw.Window, xpos, ypos float32)
13 | ScrollCallback(window *glfw.Window, xoffset, yoffset float32)
14 | }
15 |
16 | type KeyAction struct {
17 | key controller.Key
18 | action controller.Action
19 | }
20 |
21 | type MouseButtonAction struct {
22 | button controller.MouseButton
23 | action controller.Action
24 | }
25 |
26 | type ActionMap struct {
27 | keyAction func(key controller.Key, action controller.Action)
28 | mouseAction func(button controller.MouseButton, action controller.Action)
29 | keyActionMap map[KeyAction][]func()
30 | mouseButtonActionMap map[MouseButtonAction][]func()
31 | axisActions []func(xpos, ypos float32)
32 | scrollActions []func(xoffset, yoffset float32)
33 | }
34 |
35 | func NewActionMap() controller.Controller {
36 | am := &ActionMap{
37 | keyAction: func(key controller.Key, action controller.Action) {},
38 | mouseAction: func(button controller.MouseButton, action controller.Action) {},
39 | keyActionMap: make(map[KeyAction][]func()),
40 | mouseButtonActionMap: make(map[MouseButtonAction][]func()),
41 | axisActions: make([]func(xpos, ypos float32), 0, 0),
42 | scrollActions: make([]func(xoffset, yoffset float32), 0, 0),
43 | }
44 | return am
45 | }
46 |
47 | func (am *ActionMap) SetKeyAction(function func(key controller.Key, action controller.Action)) {
48 | am.keyAction = function
49 | }
50 |
51 | func (am *ActionMap) SetMouseAction(function func(button controller.MouseButton, action controller.Action)) {
52 | am.mouseAction = function
53 | }
54 |
55 | //Bindings
56 | func (am *ActionMap) BindKeyAction(function func(), key controller.Key, action controller.Action) {
57 | ka := KeyAction{key, action}
58 | if m, ok := am.keyActionMap[ka]; ok {
59 | am.keyActionMap[ka] = append(m, function)
60 | } else {
61 | am.keyActionMap[ka] = []func(){function}
62 | }
63 | }
64 |
65 | func (am *ActionMap) BindMouseAction(function func(), button controller.MouseButton, action controller.Action) {
66 | mba := MouseButtonAction{button, action}
67 | if m, ok := am.mouseButtonActionMap[mba]; ok {
68 | am.mouseButtonActionMap[mba] = append(m, function)
69 | } else {
70 | am.mouseButtonActionMap[mba] = []func(){function}
71 | }
72 | }
73 |
74 | func (am *ActionMap) BindAxisAction(function func(xpos, ypos float32)) {
75 | am.axisActions = append(am.axisActions, function)
76 | }
77 |
78 | func (am *ActionMap) BindScrollAction(function func(xoffset, yoffset float32)) {
79 | am.scrollActions = append(am.scrollActions, function)
80 | }
81 |
82 | //Callbacks
83 | func (am *ActionMap) KeyCallback(window *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
84 | am.keyAction(getKey(key), getAction(action))
85 | ka := KeyAction{getKey(key), getAction(action)}
86 | if m, ok := am.keyActionMap[ka]; ok {
87 | for _, function := range m {
88 | function()
89 | }
90 | }
91 | }
92 |
93 | func (am *ActionMap) MouseButtonCallback(window *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
94 | am.mouseAction(getMouseButton(button), getAction(action))
95 | mba := MouseButtonAction{getMouseButton(button), getAction(action)}
96 | if m, ok := am.mouseButtonActionMap[mba]; ok {
97 | for _, function := range m {
98 | function()
99 | }
100 | }
101 | }
102 |
103 | func (am *ActionMap) CursorPosCallback(window *glfw.Window, xpos, ypos float32) {
104 | for _, action := range am.axisActions {
105 | action(xpos, ypos)
106 | }
107 | }
108 |
109 | func (am *ActionMap) ScrollCallback(window *glfw.Window, xoffset, yoffset float32) {
110 | for _, action := range am.scrollActions {
111 | action(xoffset, yoffset)
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/glfwController/controllerManager.go:
--------------------------------------------------------------------------------
1 | package glfwController
2 |
3 | import (
4 | "github.com/go-gl/glfw/v3.1/glfw"
5 | )
6 |
7 | type ControllerManager struct {
8 | controllerList []Controller
9 | }
10 |
11 | //Key Callback
12 | func (c *ControllerManager) KeyCallback(window *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
13 | for _, cont := range c.controllerList {
14 | cont.KeyCallback(window, key, scancode, action, mods)
15 | }
16 | }
17 |
18 | //Mouse click callback
19 | func (c *ControllerManager) MouseButtonCallback(window *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
20 | for _, cont := range c.controllerList {
21 | cont.MouseButtonCallback(window, button, action, mods)
22 | }
23 | }
24 |
25 | //Mouse movement callback
26 | func (c *ControllerManager) CursorPosCallback(window *glfw.Window, xpos, ypos float64) {
27 | for _, cont := range c.controllerList {
28 | cont.CursorPosCallback(window, float32(xpos), float32(ypos))
29 | }
30 | }
31 |
32 | //Mouse scrollwheel callback
33 | func (c *ControllerManager) ScrollCallback(window *glfw.Window, xoffset, yoffset float64) {
34 | for _, cont := range c.controllerList {
35 | cont.ScrollCallback(window, float32(xoffset), float32(yoffset))
36 | }
37 | }
38 |
39 | func (c *ControllerManager) AddController(newCont Controller) {
40 | c.controllerList = append(c.controllerList, newCont)
41 | }
42 |
43 | func (c *ControllerManager) RemoveController(controller Controller) {
44 | for index, cont := range c.controllerList {
45 | if cont == controller {
46 | c.controllerList = append(c.controllerList[:index], c.controllerList[index+1:]...)
47 | }
48 | }
49 | }
50 |
51 | func NewControllerManager(window *glfw.Window) *ControllerManager {
52 | var controllerList []Controller
53 | c := &ControllerManager{controllerList}
54 | window.SetKeyCallback(c.KeyCallback)
55 | window.SetMouseButtonCallback(c.MouseButtonCallback)
56 | window.SetCursorPosCallback(c.CursorPosCallback)
57 | window.SetScrollCallback(c.ScrollCallback)
58 | return c
59 | }
60 |
--------------------------------------------------------------------------------
/glfwController/controller_test.go:
--------------------------------------------------------------------------------
1 | package glfwController
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "testing"
7 |
8 | "github.com/go-gl/glfw/v3.1/glfw"
9 | "github.com/stretchr/testify/assert"
10 | "github.com/walesey/go-engine/controller"
11 | )
12 |
13 | type TestObject struct {
14 | testState bool
15 | }
16 |
17 | func (to *TestObject) testAction() {
18 | to.testState = true
19 | }
20 | func (to *TestObject) otherTestAction() {
21 | to.testState = false
22 | }
23 |
24 | func TestMain(m *testing.M) {
25 | os.Exit(m.Run())
26 | }
27 |
28 | func TestActionMap(t *testing.T) {
29 | var controllerList []Controller
30 | manager := &ControllerManager{controllerList}
31 |
32 | to := TestObject{false}
33 | c := NewActionMap()
34 | c.BindKeyAction(to.testAction, controller.KeyW, controller.Press)
35 | c.BindKeyAction(to.otherTestAction, controller.KeyE, controller.Release)
36 | manager.AddController(c.(Controller))
37 |
38 | fmt.Println("About to trigger custom actions")
39 | manager.KeyCallback(nil, glfw.KeyW, 0, glfw.Press, 0)
40 | assert.True(t, to.testState, "test state not triggered by key binding")
41 | manager.KeyCallback(nil, glfw.KeyE, 0, glfw.Release, 0)
42 | assert.False(t, to.testState, "test state not triggered by key binding")
43 | }
44 |
--------------------------------------------------------------------------------
/glfwController/inputMappings.go:
--------------------------------------------------------------------------------
1 | package glfwController
2 |
3 | import (
4 | "github.com/go-gl/glfw/v3.1/glfw"
5 | "github.com/walesey/go-engine/controller"
6 | )
7 |
8 | func getJoystick(joystick glfw.Joystick) controller.Joystick {
9 | return controller.Joystick(joystick)
10 | }
11 |
12 | func getKey(key glfw.Key) controller.Key {
13 | return controller.Key(key)
14 | }
15 |
16 | func getModifierKey(key glfw.ModifierKey) controller.ModifierKey {
17 | return controller.ModifierKey(key)
18 | }
19 |
20 | func getMouseButton(mouseButton glfw.MouseButton) controller.MouseButton {
21 | return controller.MouseButton(mouseButton)
22 | }
23 |
24 | func getAction(mouseButton glfw.Action) controller.Action {
25 | return controller.Action(mouseButton)
26 | }
27 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/walesey/go-engine
2 |
3 | go 1.17
4 |
5 | require (
6 | github.com/Invictus321/invictus321-countdown v0.0.0-20160831012427-1226ab952c2f // indirect
7 | github.com/aymerick/douceur v0.2.0 // indirect
8 | github.com/disintegration/imaging v1.6.2 // indirect
9 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
10 | github.com/go-gl/glfw v0.0.0-20211213063430-748e38ca8aec // indirect
11 | github.com/go-gl/mathgl v1.0.0 // indirect
12 | github.com/gorilla/css v1.0.0 // indirect
13 | github.com/pkg/errors v0.9.1 // indirect
14 | github.com/sirupsen/logrus v1.8.1 // indirect
15 | github.com/vova616/chipmunk v0.0.0-20180914035118-c3710bbc8933 // indirect
16 | github.com/walesey/go-fileserver v0.0.0-20180813233004-768d611d4fb8 // indirect
17 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
18 | golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect
19 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da // indirect
20 | )
21 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Invictus321/invictus321-countdown v0.0.0-20160831012427-1226ab952c2f h1:wXYsTpGdioJ2a8/Y7oU2tlqeFD3OsGQ9sLjk8ih2QVw=
2 | github.com/Invictus321/invictus321-countdown v0.0.0-20160831012427-1226ab952c2f/go.mod h1:Ry4yXbmmNb29MHV18ojJ4tqsLaE3cIy7tCLTzgryNvo=
3 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
4 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
7 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
8 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
9 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
10 | github.com/go-gl/glfw v0.0.0-20211213063430-748e38ca8aec h1:um18JldLG6QwC9tj6mSfQnb+kor5aezfPPtq1GmHek0=
11 | github.com/go-gl/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
12 | github.com/go-gl/mathgl v1.0.0 h1:t9DznWJlXxxjeeKLIdovCOVJQk/GzDEL7h/h+Ro2B68=
13 | github.com/go-gl/mathgl v1.0.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
14 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
15 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
16 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
17 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
19 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
20 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
21 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
22 | github.com/vova616/chipmunk v0.0.0-20180914035118-c3710bbc8933 h1:/0NyCSo8ffYPuc6KyanXavxaF3D1YE16PrrmtOnXqGk=
23 | github.com/vova616/chipmunk v0.0.0-20180914035118-c3710bbc8933/go.mod h1:ptbc/tNoFjKw1fVLyvtXQwISNbF7EJ3abLsKI+LFUEk=
24 | github.com/walesey/go-fileserver v0.0.0-20180813233004-768d611d4fb8 h1:emFMqxWXJgOmSdMUOIF9OL3ac8dL5ZRyuHMpYKOWntI=
25 | github.com/walesey/go-fileserver v0.0.0-20180813233004-768d611d4fb8/go.mod h1:tMqjDicUoKiNJfjBsRouCYUPo5tbOu7AoNCcEc1C22E=
26 | golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
27 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
28 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
29 | golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d h1:1n1fc535VhN8SYtD4cDUyNlfpAF2ROMM9+11equK3hs=
30 | golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
31 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
32 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
33 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
34 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
35 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
36 |
--------------------------------------------------------------------------------
/libs/freetype/AUTHORS:
--------------------------------------------------------------------------------
1 | # This is the official list of Freetype-Go authors for copyright purposes.
2 | # This file is distinct from the CONTRIBUTORS files.
3 | # See the latter for an explanation.
4 | #
5 | # Freetype-Go is derived from Freetype, which is written in C. The latter
6 | # is copyright 1996-2010 David Turner, Robert Wilhelm, and Werner Lemberg.
7 |
8 | # Names should be added to this file as
9 | # Name or Organization
10 | # The email address is not required for organizations.
11 |
12 | # Please keep the list sorted.
13 |
14 | Google Inc.
15 | Jeff R. Allen
16 | Rémy Oudompheng
17 | Roger Peppe
18 | Steven Edwards
19 |
--------------------------------------------------------------------------------
/libs/freetype/CONTRIBUTORS:
--------------------------------------------------------------------------------
1 | # This is the official list of people who can contribute
2 | # (and typically have contributed) code to the Freetype-Go repository.
3 | # The AUTHORS file lists the copyright holders; this file
4 | # lists people. For example, Google employees are listed here
5 | # but not in AUTHORS, because Google holds the copyright.
6 | #
7 | # The submission process automatically checks to make sure
8 | # that people submitting code are listed in this file (by email address).
9 | #
10 | # Names should be added to this file only after verifying that
11 | # the individual or the individual's organization has agreed to
12 | # the appropriate Contributor License Agreement, found here:
13 | #
14 | # http://code.google.com/legal/individual-cla-v1.0.html
15 | # http://code.google.com/legal/corporate-cla-v1.0.html
16 | #
17 | # The agreement for individuals can be filled out on the web.
18 | #
19 | # When adding J Random Contributor's name to this file,
20 | # either J's name or J's organization's name should be
21 | # added to the AUTHORS file, depending on whether the
22 | # individual or corporate CLA was used.
23 |
24 | # Names should be added to this file like so:
25 | # Name
26 |
27 | # Please keep the list sorted.
28 |
29 | Andrew Gerrand
30 | Jeff R. Allen
31 | Nigel Tao
32 | Rémy Oudompheng
33 | Rob Pike
34 | Roger Peppe
35 | Russ Cox
36 | Steven Edwards
37 |
--------------------------------------------------------------------------------
/libs/freetype/LICENSE:
--------------------------------------------------------------------------------
1 | Use of the Freetype-Go software is subject to your choice of exactly one of
2 | the following two licenses:
3 | * The FreeType License, which is similar to the original BSD license with
4 | an advertising clause, or
5 | * The GNU General Public License (GPL), version 2 or later.
6 |
7 | The text of these licenses are available in the licenses/ftl.txt and the
8 | licenses/gpl.txt files respectively. They are also available at
9 | http://freetype.sourceforge.net/license.html
10 |
11 | The Luxi fonts in the testdata directory are licensed separately. See the
12 | testdata/COPYING file for details.
13 |
--------------------------------------------------------------------------------
/libs/freetype/README:
--------------------------------------------------------------------------------
1 | The Freetype font rasterizer in the Go programming language.
2 |
3 | To download and install from source:
4 | $ go get github.com/golang/freetype
5 |
6 | It is an incomplete port:
7 | * It only supports TrueType fonts, and not Type 1 fonts nor bitmap fonts.
8 | * It only supports the Unicode encoding.
9 |
10 | There are also some implementation differences:
11 | * It uses a 26.6 fixed point co-ordinate system everywhere internally,
12 | as opposed to the original Freetype's mix of 26.6 (or 10.6 for 16-bit
13 | systems) in some places, and 24.8 in the "smooth" rasterizer.
14 |
15 | Freetype-Go is derived from Freetype, which is written in C. Freetype is
16 | copyright 1996-2010 David Turner, Robert Wilhelm, and Werner Lemberg.
17 | Freetype-Go is copyright The Freetype-Go Authors, who are listed in the
18 | AUTHORS file.
19 |
20 | Unless otherwise noted, the Freetype-Go source files are distributed
21 | under the BSD-style license found in the LICENSE file.
22 |
--------------------------------------------------------------------------------
/libs/freetype/cmd/print-glyph-points/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | gcc main.c -I/usr/include/freetype2 -lfreetype && ./a.out 12 ../../testdata/luxisr.ttf with_hinting
3 | */
4 |
5 | #include
6 | #include
7 | #include FT_FREETYPE_H
8 |
9 | void usage(char** argv) {
10 | fprintf(stderr, "usage: %s font_size font_file [with_hinting|sans_hinting]\n", argv[0]);
11 | }
12 |
13 | int main(int argc, char** argv) {
14 | FT_Error error;
15 | FT_Library library;
16 | FT_Face face;
17 | FT_Glyph_Metrics* m;
18 | FT_Outline* o;
19 | FT_Int major, minor, patch;
20 | int i, j, font_size, no_hinting;
21 |
22 | if (argc != 4) {
23 | usage(argv);
24 | return 1;
25 | }
26 | font_size = atoi(argv[1]);
27 | if (font_size <= 0) {
28 | fprintf(stderr, "invalid font_size\n");
29 | usage(argv);
30 | return 1;
31 | }
32 | if (!strcmp(argv[3], "with_hinting")) {
33 | no_hinting = 0;
34 | } else if (!strcmp(argv[3], "sans_hinting")) {
35 | no_hinting = 1;
36 | } else {
37 | fprintf(stderr, "neither \"with_hinting\" nor \"sans_hinting\"\n");
38 | usage(argv);
39 | return 1;
40 | };
41 | error = FT_Init_FreeType(&library);
42 | if (error) {
43 | fprintf(stderr, "FT_Init_FreeType: error #%d\n", error);
44 | return 1;
45 | }
46 | FT_Library_Version(library, &major, &minor, &patch);
47 | printf("freetype version %d.%d.%d\n", major, minor, patch);
48 | error = FT_New_Face(library, argv[2], 0, &face);
49 | if (error) {
50 | fprintf(stderr, "FT_New_Face: error #%d\n", error);
51 | return 1;
52 | }
53 | error = FT_Set_Char_Size(face, 0, font_size*64, 0, 0);
54 | if (error) {
55 | fprintf(stderr, "FT_Set_Char_Size: error #%d\n", error);
56 | return 1;
57 | }
58 | for (i = 0; i < face->num_glyphs; i++) {
59 | error = FT_Load_Glyph(face, i, no_hinting ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT);
60 | if (error) {
61 | fprintf(stderr, "FT_Load_Glyph: glyph %d: error #%d\n", i, error);
62 | return 1;
63 | }
64 | if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
65 | fprintf(stderr, "glyph format for glyph %d is not FT_GLYPH_FORMAT_OUTLINE\n", i);
66 | return 1;
67 | }
68 | m = &face->glyph->metrics;
69 | /* Print what Go calls the AdvanceWidth, and then: XMin, YMin, XMax, YMax. */
70 | printf("%ld %ld %ld %ld %ld;",
71 | m->horiAdvance,
72 | m->horiBearingX,
73 | m->horiBearingY - m->height,
74 | m->horiBearingX + m->width,
75 | m->horiBearingY);
76 | /* Print the glyph points. */
77 | o = &face->glyph->outline;
78 | for (j = 0; j < o->n_points; j++) {
79 | if (j != 0) {
80 | printf(", ");
81 | }
82 | printf("%ld %ld %d", o->points[j].x, o->points[j].y, o->tags[j] & 0x01);
83 | }
84 | printf("\n");
85 | }
86 | return 0;
87 | }
88 |
--------------------------------------------------------------------------------
/libs/freetype/example/drawer/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The Freetype-Go Authors. All rights reserved.
2 | // Use of this source code is governed by your choice of either the
3 | // FreeType License or the GNU General Public License version 2 (or
4 | // any later version), both of which can be found in the LICENSE file.
5 |
6 | // +build ignore
7 | //
8 | // This build tag means that "go install github.com/walesey/go-engine/libs/freetype/..."
9 | // doesn't install this example program. Use "go run main.go" to run it.
10 |
11 | package main
12 |
13 | import (
14 | "bufio"
15 | "flag"
16 | "fmt"
17 | "image"
18 | "image/color"
19 | "image/draw"
20 | "image/png"
21 | "io/ioutil"
22 | "log"
23 | "math"
24 | "os"
25 |
26 | "github.com/walesey/go-engine/libs/freetype/truetype"
27 | "golang.org/x/image/font"
28 | "golang.org/x/image/math/fixed"
29 | )
30 |
31 | var (
32 | dpi = flag.Float64("dpi", 72, "screen resolution in Dots Per Inch")
33 | fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font")
34 | hinting = flag.String("hinting", "none", "none | full")
35 | size = flag.Float64("size", 12, "font size in points")
36 | spacing = flag.Float64("spacing", 1.5, "line spacing (e.g. 2 means double spaced)")
37 | wonb = flag.Bool("whiteonblack", false, "white text on a black background")
38 | )
39 |
40 | const title = "Jabberwocky"
41 |
42 | var text = []string{
43 | "’Twas brillig, and the slithy toves",
44 | "Did gyre and gimble in the wabe;",
45 | "All mimsy were the borogoves,",
46 | "And the mome raths outgrabe.",
47 | "",
48 | "“Beware the Jabberwock, my son!",
49 | "The jaws that bite, the claws that catch!",
50 | "Beware the Jubjub bird, and shun",
51 | "The frumious Bandersnatch!”",
52 | "",
53 | "He took his vorpal sword in hand:",
54 | "Long time the manxome foe he sought—",
55 | "So rested he by the Tumtum tree,",
56 | "And stood awhile in thought.",
57 | "",
58 | "And as in uffish thought he stood,",
59 | "The Jabberwock, with eyes of flame,",
60 | "Came whiffling through the tulgey wood,",
61 | "And burbled as it came!",
62 | "",
63 | "One, two! One, two! and through and through",
64 | "The vorpal blade went snicker-snack!",
65 | "He left it dead, and with its head",
66 | "He went galumphing back.",
67 | "",
68 | "“And hast thou slain the Jabberwock?",
69 | "Come to my arms, my beamish boy!",
70 | "O frabjous day! Callooh! Callay!”",
71 | "He chortled in his joy.",
72 | "",
73 | "’Twas brillig, and the slithy toves",
74 | "Did gyre and gimble in the wabe;",
75 | "All mimsy were the borogoves,",
76 | "And the mome raths outgrabe.",
77 | }
78 |
79 | func main() {
80 | flag.Parse()
81 |
82 | // Read the font data.
83 | fontBytes, err := ioutil.ReadFile(*fontfile)
84 | if err != nil {
85 | log.Println(err)
86 | return
87 | }
88 | f, err := truetype.Parse(fontBytes)
89 | if err != nil {
90 | log.Println(err)
91 | return
92 | }
93 |
94 | // Draw the background and the guidelines.
95 | fg, bg := image.Black, image.White
96 | ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff}
97 | if *wonb {
98 | fg, bg = image.White, image.Black
99 | ruler = color.RGBA{0x22, 0x22, 0x22, 0xff}
100 | }
101 | const imgW, imgH = 640, 480
102 | rgba := image.NewRGBA(image.Rect(0, 0, imgW, imgH))
103 | draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)
104 | for i := 0; i < 200; i++ {
105 | rgba.Set(10, 10+i, ruler)
106 | rgba.Set(10+i, 10, ruler)
107 | }
108 |
109 | // Draw the text.
110 | h := font.HintingNone
111 | switch *hinting {
112 | case "full":
113 | h = font.HintingFull
114 | }
115 | d := &font.Drawer{
116 | Dst: rgba,
117 | Src: fg,
118 | Face: truetype.NewFace(f, &truetype.Options{
119 | Size: *size,
120 | DPI: *dpi,
121 | Hinting: h,
122 | }),
123 | }
124 | y := 10 + int(math.Ceil(*size**dpi/72))
125 | dy := int(math.Ceil(*size * *spacing * *dpi / 72))
126 | d.Dot = fixed.Point26_6{
127 | X: (fixed.I(imgW) - d.MeasureString(title)) / 2,
128 | Y: fixed.I(y),
129 | }
130 | d.DrawString(title)
131 | y += dy
132 | for _, s := range text {
133 | d.Dot = fixed.P(10, y)
134 | d.DrawString(s)
135 | y += dy
136 | }
137 |
138 | // Save that RGBA image to disk.
139 | outFile, err := os.Create("out.png")
140 | if err != nil {
141 | log.Println(err)
142 | os.Exit(1)
143 | }
144 | defer outFile.Close()
145 | b := bufio.NewWriter(outFile)
146 | err = png.Encode(b, rgba)
147 | if err != nil {
148 | log.Println(err)
149 | os.Exit(1)
150 | }
151 | err = b.Flush()
152 | if err != nil {
153 | log.Println(err)
154 | os.Exit(1)
155 | }
156 | fmt.Println("Wrote out.png OK.")
157 | }
158 |
--------------------------------------------------------------------------------
/libs/freetype/example/drawer/out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/libs/freetype/example/drawer/out.png
--------------------------------------------------------------------------------
/libs/freetype/example/freetype/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Freetype-Go Authors. All rights reserved.
2 | // Use of this source code is governed by your choice of either the
3 | // FreeType License or the GNU General Public License version 2 (or
4 | // any later version), both of which can be found in the LICENSE file.
5 |
6 | // +build ignore
7 | //
8 | // This build tag means that "go install github.com/walesey/go-engine/libs/freetype/..."
9 | // doesn't install this example program. Use "go run main.go" to run it.
10 |
11 | package main
12 |
13 | import (
14 | "bufio"
15 | "flag"
16 | "fmt"
17 | "image"
18 | "image/draw"
19 | "image/png"
20 | "io/ioutil"
21 | "log"
22 | "os"
23 |
24 | "github.com/walesey/go-engine/libs/freetype"
25 | "golang.org/x/image/font"
26 | "golang.org/x/image/math/fixed"
27 | )
28 |
29 | var (
30 | dpi = flag.Float64("dpi", 75, "screen resolution in Dots Per Inch")
31 | fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font")
32 | hinting = flag.String("hinting", "none", "none | full")
33 | size = flag.Float64("size", 12, "font size in points")
34 | spacing = flag.Float64("spacing", 1.5, "line spacing (e.g. 2 means double spaced)")
35 | wonb = flag.Bool("whiteonblack", false, "white text on a black background")
36 | )
37 |
38 | var text = []string{
39 | "’Twas Twas Twas Twas www",
40 | }
41 |
42 | func main() {
43 | flag.Parse()
44 |
45 | // Read the font data.
46 | fontBytes, err := ioutil.ReadFile(*fontfile)
47 | if err != nil {
48 | log.Println(err)
49 | return
50 | }
51 | f, err := freetype.ParseFont(fontBytes)
52 | if err != nil {
53 | log.Println(err)
54 | return
55 | }
56 |
57 | // Initialize the context.
58 | fg, bg := image.Black, image.White
59 | if *wonb {
60 | fg, bg = image.White, image.Black
61 | }
62 | c := freetype.NewContext()
63 | c.SetDPI(*dpi)
64 | c.SetFont(f)
65 | c.SetFontSize(*size)
66 | c.SetSrc(fg)
67 | switch *hinting {
68 | default:
69 | c.SetHinting(font.HintingNone)
70 | case "full":
71 | c.SetHinting(font.HintingFull)
72 | }
73 |
74 | var width fixed.Int26_6
75 | var height fixed.Int26_6
76 | for _, s := range text {
77 | dimensions, _ := c.StringDimensions(s)
78 | height = height + dimensions.Y
79 | if dimensions.X > width {
80 | width = dimensions.X
81 | }
82 | }
83 | imgWidth := int(width >> 6)
84 | imgHeight := int(height >> 6)
85 | fmt.Println(imgWidth, imgHeight)
86 | rgba := image.NewRGBA(image.Rect(0, 0, imgWidth, imgHeight))
87 | draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)
88 | c.SetClip(rgba.Bounds())
89 | c.SetDst(rgba)
90 |
91 | // Draw the text.
92 | pt := freetype.Pt(0, int(c.PointToFixed(*size)>>6))
93 | for _, s := range text {
94 | _, err = c.DrawString(s, pt)
95 | if err != nil {
96 | log.Println(err)
97 | return
98 | }
99 | pt.Y += c.PointToFixed(*size * *spacing)
100 | }
101 |
102 | // Save that RGBA image to disk.
103 | outFile, err := os.Create("out.png")
104 | if err != nil {
105 | log.Println(err)
106 | os.Exit(1)
107 | }
108 | defer outFile.Close()
109 | b := bufio.NewWriter(outFile)
110 | err = png.Encode(b, rgba)
111 | if err != nil {
112 | log.Println(err)
113 | os.Exit(1)
114 | }
115 | err = b.Flush()
116 | if err != nil {
117 | log.Println(err)
118 | os.Exit(1)
119 | }
120 | fmt.Println("Wrote out.png OK.")
121 | }
122 |
--------------------------------------------------------------------------------
/libs/freetype/example/freetype/out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/libs/freetype/example/freetype/out.png
--------------------------------------------------------------------------------
/libs/freetype/example/gamma/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Freetype-Go Authors. All rights reserved.
2 | // Use of this source code is governed by your choice of either the
3 | // FreeType License or the GNU General Public License version 2 (or
4 | // any later version), both of which can be found in the LICENSE file.
5 |
6 | // +build ignore
7 | //
8 | // This build tag means that "go install github.com/walesey/go-engine/libs/freetype/..."
9 | // doesn't install this example program. Use "go run main.go" to run it.
10 |
11 | package main
12 |
13 | import (
14 | "bufio"
15 | "fmt"
16 | "image"
17 | "image/draw"
18 | "image/png"
19 | "log"
20 | "os"
21 |
22 | "github.com/walesey/go-engine/libs/freetype/raster"
23 | "golang.org/x/image/math/fixed"
24 | )
25 |
26 | func p(x, y int) fixed.Point26_6 {
27 | return fixed.Point26_6{
28 | X: fixed.Int26_6(x * 64),
29 | Y: fixed.Int26_6(y * 64),
30 | }
31 | }
32 |
33 | func main() {
34 | // Draw a rounded corner that is one pixel wide.
35 | r := raster.NewRasterizer(50, 50)
36 | r.Start(p(5, 5))
37 | r.Add1(p(5, 25))
38 | r.Add2(p(5, 45), p(25, 45))
39 | r.Add1(p(45, 45))
40 | r.Add1(p(45, 44))
41 | r.Add1(p(26, 44))
42 | r.Add2(p(6, 44), p(6, 24))
43 | r.Add1(p(6, 5))
44 | r.Add1(p(5, 5))
45 |
46 | // Rasterize that curve multiple times at different gammas.
47 | const (
48 | w = 600
49 | h = 200
50 | )
51 | rgba := image.NewRGBA(image.Rect(0, 0, w, h))
52 | draw.Draw(rgba, image.Rect(0, 0, w, h/2), image.Black, image.ZP, draw.Src)
53 | draw.Draw(rgba, image.Rect(0, h/2, w, h), image.White, image.ZP, draw.Src)
54 | mask := image.NewAlpha(image.Rect(0, 0, 50, 50))
55 | painter := raster.NewAlphaSrcPainter(mask)
56 | gammas := []float64{1.0 / 10.0, 1.0 / 3.0, 1.0 / 2.0, 2.0 / 3.0, 4.0 / 5.0, 1.0, 5.0 / 4.0, 3.0 / 2.0, 2.0, 3.0, 10.0}
57 | for i, g := range gammas {
58 | draw.Draw(mask, mask.Bounds(), image.Transparent, image.ZP, draw.Src)
59 | r.Rasterize(raster.NewGammaCorrectionPainter(painter, g))
60 | x, y := 50*i+25, 25
61 | draw.DrawMask(rgba, image.Rect(x, y, x+50, y+50), image.White, image.ZP, mask, image.ZP, draw.Over)
62 | y += 100
63 | draw.DrawMask(rgba, image.Rect(x, y, x+50, y+50), image.Black, image.ZP, mask, image.ZP, draw.Over)
64 | }
65 |
66 | // Save that RGBA image to disk.
67 | outFile, err := os.Create("out.png")
68 | if err != nil {
69 | log.Println(err)
70 | os.Exit(1)
71 | }
72 | defer outFile.Close()
73 | b := bufio.NewWriter(outFile)
74 | err = png.Encode(b, rgba)
75 | if err != nil {
76 | log.Println(err)
77 | os.Exit(1)
78 | }
79 | err = b.Flush()
80 | if err != nil {
81 | log.Println(err)
82 | os.Exit(1)
83 | }
84 | fmt.Println("Wrote out.png OK.")
85 | }
86 |
--------------------------------------------------------------------------------
/libs/freetype/example/raster/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Freetype-Go Authors. All rights reserved.
2 | // Use of this source code is governed by your choice of either the
3 | // FreeType License or the GNU General Public License version 2 (or
4 | // any later version), both of which can be found in the LICENSE file.
5 |
6 | // +build ignore
7 | //
8 | // This build tag means that "go install github.com/walesey/go-engine/libs/freetype/..."
9 | // doesn't install this example program. Use "go run main.go" to run it.
10 |
11 | package main
12 |
13 | import (
14 | "bufio"
15 | "fmt"
16 | "image"
17 | "image/color"
18 | "image/draw"
19 | "image/png"
20 | "log"
21 | "os"
22 |
23 | "github.com/walesey/go-engine/libs/freetype/raster"
24 | "golang.org/x/image/math/fixed"
25 | )
26 |
27 | type node struct {
28 | x, y, degree int
29 | }
30 |
31 | // These contours "outside" and "inside" are from the 'A' glyph from the Droid
32 | // Serif Regular font.
33 |
34 | var outside = []node{
35 | node{414, 489, 1},
36 | node{336, 274, 2},
37 | node{327, 250, 0},
38 | node{322, 226, 2},
39 | node{317, 203, 0},
40 | node{317, 186, 2},
41 | node{317, 134, 0},
42 | node{350, 110, 2},
43 | node{384, 86, 0},
44 | node{453, 86, 1},
45 | node{500, 86, 1},
46 | node{500, 0, 1},
47 | node{0, 0, 1},
48 | node{0, 86, 1},
49 | node{39, 86, 2},
50 | node{69, 86, 0},
51 | node{90, 92, 2},
52 | node{111, 99, 0},
53 | node{128, 117, 2},
54 | node{145, 135, 0},
55 | node{160, 166, 2},
56 | node{176, 197, 0},
57 | node{195, 246, 1},
58 | node{649, 1462, 1},
59 | node{809, 1462, 1},
60 | node{1272, 195, 2},
61 | node{1284, 163, 0},
62 | node{1296, 142, 2},
63 | node{1309, 121, 0},
64 | node{1326, 108, 2},
65 | node{1343, 96, 0},
66 | node{1365, 91, 2},
67 | node{1387, 86, 0},
68 | node{1417, 86, 1},
69 | node{1444, 86, 1},
70 | node{1444, 0, 1},
71 | node{881, 0, 1},
72 | node{881, 86, 1},
73 | node{928, 86, 2},
74 | node{1051, 86, 0},
75 | node{1051, 184, 2},
76 | node{1051, 201, 0},
77 | node{1046, 219, 2},
78 | node{1042, 237, 0},
79 | node{1034, 260, 1},
80 | node{952, 489, 1},
81 | node{414, 489, -1},
82 | }
83 |
84 | var inside = []node{
85 | node{686, 1274, 1},
86 | node{453, 592, 1},
87 | node{915, 592, 1},
88 | node{686, 1274, -1},
89 | }
90 |
91 | func p(n node) fixed.Point26_6 {
92 | x, y := 20+n.x/4, 380-n.y/4
93 | return fixed.Point26_6{
94 | X: fixed.Int26_6(x << 6),
95 | Y: fixed.Int26_6(y << 6),
96 | }
97 | }
98 |
99 | func contour(r *raster.Rasterizer, ns []node) {
100 | if len(ns) == 0 {
101 | return
102 | }
103 | i := 0
104 | r.Start(p(ns[i]))
105 | for {
106 | switch ns[i].degree {
107 | case -1:
108 | // -1 signifies end-of-contour.
109 | return
110 | case 1:
111 | i += 1
112 | r.Add1(p(ns[i]))
113 | case 2:
114 | i += 2
115 | r.Add2(p(ns[i-1]), p(ns[i]))
116 | default:
117 | panic("bad degree")
118 | }
119 | }
120 | }
121 |
122 | func showNodes(m *image.RGBA, ns []node) {
123 | for _, n := range ns {
124 | p := p(n)
125 | x, y := int(p.X)/64, int(p.Y)/64
126 | if !(image.Point{x, y}).In(m.Bounds()) {
127 | continue
128 | }
129 | var c color.Color
130 | switch n.degree {
131 | case 0:
132 | c = color.RGBA{0, 255, 255, 255}
133 | case 1:
134 | c = color.RGBA{255, 0, 0, 255}
135 | case 2:
136 | c = color.RGBA{255, 0, 0, 255}
137 | }
138 | if c != nil {
139 | m.Set(x, y, c)
140 | }
141 | }
142 | }
143 |
144 | func main() {
145 | // Rasterize the contours to a mask image.
146 | const (
147 | w = 400
148 | h = 400
149 | )
150 | r := raster.NewRasterizer(w, h)
151 | contour(r, outside)
152 | contour(r, inside)
153 | mask := image.NewAlpha(image.Rect(0, 0, w, h))
154 | p := raster.NewAlphaSrcPainter(mask)
155 | r.Rasterize(p)
156 |
157 | // Draw the mask image (in gray) onto an RGBA image.
158 | rgba := image.NewRGBA(image.Rect(0, 0, w, h))
159 | gray := image.NewUniform(color.Alpha{0x1f})
160 | draw.Draw(rgba, rgba.Bounds(), image.Black, image.ZP, draw.Src)
161 | draw.DrawMask(rgba, rgba.Bounds(), gray, image.ZP, mask, image.ZP, draw.Over)
162 | showNodes(rgba, outside)
163 | showNodes(rgba, inside)
164 |
165 | // Save that RGBA image to disk.
166 | outFile, err := os.Create("out.png")
167 | if err != nil {
168 | log.Println(err)
169 | os.Exit(1)
170 | }
171 | defer outFile.Close()
172 | b := bufio.NewWriter(outFile)
173 | err = png.Encode(b, rgba)
174 | if err != nil {
175 | log.Println(err)
176 | os.Exit(1)
177 | }
178 | err = b.Flush()
179 | if err != nil {
180 | log.Println(err)
181 | os.Exit(1)
182 | }
183 | fmt.Println("Wrote out.png OK.")
184 | }
185 |
--------------------------------------------------------------------------------
/libs/freetype/example/round/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Freetype-Go Authors. All rights reserved.
2 | // Use of this source code is governed by your choice of either the
3 | // FreeType License or the GNU General Public License version 2 (or
4 | // any later version), both of which can be found in the LICENSE file.
5 |
6 | // +build ignore
7 | //
8 | // This build tag means that "go install github.com/walesey/go-engine/libs/freetype/..."
9 | // doesn't install this example program. Use "go run main.go" to run it.
10 |
11 | // This program visualizes the quadratic approximation to the circle, used to
12 | // implement round joins when stroking paths. The approximation is used in the
13 | // stroking code for arcs between 0 and 45 degrees, but is visualized here
14 | // between 0 and 90 degrees. The discrepancy between the approximation and the
15 | // true circle is clearly visible at angles above 65 degrees.
16 | package main
17 |
18 | import (
19 | "bufio"
20 | "fmt"
21 | "image"
22 | "image/color"
23 | "image/draw"
24 | "image/png"
25 | "log"
26 | "math"
27 | "os"
28 |
29 | "github.com/walesey/go-engine/libs/freetype/raster"
30 | "golang.org/x/image/math/fixed"
31 | )
32 |
33 | // pDot returns the dot product p·q.
34 | func pDot(p, q fixed.Point26_6) fixed.Int52_12 {
35 | px, py := int64(p.X), int64(p.Y)
36 | qx, qy := int64(q.X), int64(q.Y)
37 | return fixed.Int52_12(px*qx + py*qy)
38 | }
39 |
40 | func main() {
41 | const (
42 | n = 17
43 | r = 64 * 80
44 | )
45 | s := fixed.Int26_6(r * math.Sqrt(2) / 2)
46 | t := fixed.Int26_6(r * math.Tan(math.Pi/8))
47 |
48 | m := image.NewRGBA(image.Rect(0, 0, 800, 600))
49 | draw.Draw(m, m.Bounds(), image.NewUniform(color.RGBA{63, 63, 63, 255}), image.ZP, draw.Src)
50 | mp := raster.NewRGBAPainter(m)
51 | mp.SetColor(image.Black)
52 | z := raster.NewRasterizer(800, 600)
53 |
54 | for i := 0; i < n; i++ {
55 | cx := fixed.Int26_6(6400 + 12800*(i%4))
56 | cy := fixed.Int26_6(640 + 8000*(i/4))
57 | c := fixed.Point26_6{X: cx, Y: cy}
58 | theta := math.Pi * (0.5 + 0.5*float64(i)/(n-1))
59 | dx := fixed.Int26_6(r * math.Cos(theta))
60 | dy := fixed.Int26_6(r * math.Sin(theta))
61 | d := fixed.Point26_6{X: dx, Y: dy}
62 | // Draw a quarter-circle approximated by two quadratic segments,
63 | // with each segment spanning 45 degrees.
64 | z.Start(c)
65 | z.Add1(c.Add(fixed.Point26_6{X: r, Y: 0}))
66 | z.Add2(c.Add(fixed.Point26_6{X: r, Y: t}), c.Add(fixed.Point26_6{X: s, Y: s}))
67 | z.Add2(c.Add(fixed.Point26_6{X: t, Y: r}), c.Add(fixed.Point26_6{X: 0, Y: r}))
68 | // Add another quadratic segment whose angle ranges between 0 and 90
69 | // degrees. For an explanation of the magic constants 128, 150, 181 and
70 | // 256, read the comments in the freetype/raster package.
71 | dot := 256 * pDot(d, fixed.Point26_6{X: 0, Y: r}) / (r * r)
72 | multiple := fixed.Int26_6(150-(150-128)*(dot-181)/(256-181)) >> 2
73 | z.Add2(c.Add(fixed.Point26_6{X: dx, Y: r + dy}.Mul(multiple)), c.Add(d))
74 | // Close the curve.
75 | z.Add1(c)
76 | }
77 | z.Rasterize(mp)
78 |
79 | for i := 0; i < n; i++ {
80 | cx := fixed.Int26_6(6400 + 12800*(i%4))
81 | cy := fixed.Int26_6(640 + 8000*(i/4))
82 | for j := 0; j < n; j++ {
83 | theta := math.Pi * float64(j) / (n - 1)
84 | dx := fixed.Int26_6(r * math.Cos(theta))
85 | dy := fixed.Int26_6(r * math.Sin(theta))
86 | m.Set(int((cx+dx)/64), int((cy+dy)/64), color.RGBA{255, 255, 0, 255})
87 | }
88 | }
89 |
90 | // Save that RGBA image to disk.
91 | outFile, err := os.Create("out.png")
92 | if err != nil {
93 | log.Println(err)
94 | os.Exit(1)
95 | }
96 | defer outFile.Close()
97 | b := bufio.NewWriter(outFile)
98 | err = png.Encode(b, m)
99 | if err != nil {
100 | log.Println(err)
101 | os.Exit(1)
102 | }
103 | err = b.Flush()
104 | if err != nil {
105 | log.Println(err)
106 | os.Exit(1)
107 | }
108 | fmt.Println("Wrote out.png OK.")
109 | }
110 |
--------------------------------------------------------------------------------
/libs/freetype/example/truetype/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Freetype-Go Authors. All rights reserved.
2 | // Use of this source code is governed by your choice of either the
3 | // FreeType License or the GNU General Public License version 2 (or
4 | // any later version), both of which can be found in the LICENSE file.
5 |
6 | // +build ignore
7 | //
8 | // This build tag means that "go install github.com/walesey/go-engine/libs/freetype/..."
9 | // doesn't install this example program. Use "go run main.go" to run it.
10 |
11 | package main
12 |
13 | import (
14 | "flag"
15 | "fmt"
16 | "io/ioutil"
17 | "log"
18 |
19 | "github.com/walesey/go-engine/libs/freetype/truetype"
20 | "golang.org/x/image/font"
21 | "golang.org/x/image/math/fixed"
22 | )
23 |
24 | var fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font")
25 |
26 | func printBounds(b fixed.Rectangle26_6) {
27 | fmt.Printf("Min.X:%d Min.Y:%d Max.X:%d Max.Y:%d\n", b.Min.X, b.Min.Y, b.Max.X, b.Max.Y)
28 | }
29 |
30 | func printGlyph(g *truetype.GlyphBuf) {
31 | printBounds(g.Bounds)
32 | fmt.Print("Points:\n---\n")
33 | e := 0
34 | for i, p := range g.Points {
35 | fmt.Printf("%4d, %4d", p.X, p.Y)
36 | if p.Flags&0x01 != 0 {
37 | fmt.Print(" on\n")
38 | } else {
39 | fmt.Print(" off\n")
40 | }
41 | if i+1 == int(g.Ends[e]) {
42 | fmt.Print("---\n")
43 | e++
44 | }
45 | }
46 | }
47 |
48 | func main() {
49 | flag.Parse()
50 | fmt.Printf("Loading fontfile %q\n", *fontfile)
51 | b, err := ioutil.ReadFile(*fontfile)
52 | if err != nil {
53 | log.Println(err)
54 | return
55 | }
56 | f, err := truetype.Parse(b)
57 | if err != nil {
58 | log.Println(err)
59 | return
60 | }
61 | fupe := fixed.Int26_6(f.FUnitsPerEm())
62 | printBounds(f.Bounds(fupe))
63 | fmt.Printf("FUnitsPerEm:%d\n\n", fupe)
64 |
65 | c0, c1 := 'A', 'V'
66 |
67 | i0 := f.Index(c0)
68 | hm := f.HMetric(fupe, i0)
69 | g := &truetype.GlyphBuf{}
70 | err = g.Load(f, fupe, i0, font.HintingNone)
71 | if err != nil {
72 | log.Println(err)
73 | return
74 | }
75 | fmt.Printf("'%c' glyph\n", c0)
76 | fmt.Printf("AdvanceWidth:%d LeftSideBearing:%d\n", hm.AdvanceWidth, hm.LeftSideBearing)
77 | printGlyph(g)
78 | i1 := f.Index(c1)
79 | fmt.Printf("\n'%c', '%c' Kern:%d\n", c0, c1, f.Kern(fupe, i0, i1))
80 | }
81 |
--------------------------------------------------------------------------------
/libs/freetype/licenses/ftl.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/libs/freetype/licenses/ftl.txt
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "runtime"
7 |
8 | "github.com/walesey/go-engine/controller"
9 | "github.com/walesey/go-engine/editor"
10 | "github.com/walesey/go-engine/glfwController"
11 | )
12 |
13 | func init() {
14 | // Use all cpu cores
15 | runtime.GOMAXPROCS(runtime.NumCPU())
16 | //Set default glfw controller
17 | controller.SetDefaultConstructor(glfwController.NewActionMap)
18 | }
19 |
20 | func main() {
21 | assetDir := "."
22 | if len(os.Args) >= 2 {
23 | assetDir = os.Args[1]
24 | }
25 |
26 | fmt.Println("Using assetDir: ", assetDir)
27 | editor.New(assetDir).Start()
28 | }
29 |
--------------------------------------------------------------------------------
/networking/client.go:
--------------------------------------------------------------------------------
1 | package networking
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "fmt"
7 | "io/ioutil"
8 | "net"
9 | "sync"
10 | )
11 |
12 | const clientPacketBufferSize = 100
13 |
14 | type Client struct {
15 | token string
16 | conn *net.UDPConn
17 | onPacketReceived func(packet Packet)
18 | bytesSent int64
19 | bytesReceived int64
20 | bytesSentByEvent map[string]int64
21 | bytesReceivedByEvent map[string]int64
22 | bytesByEventMux *sync.Mutex
23 | }
24 |
25 | func NewClient() *Client {
26 | return &Client{
27 | bytesSentByEvent: make(map[string]int64),
28 | bytesReceivedByEvent: make(map[string]int64),
29 | bytesByEventMux: &sync.Mutex{},
30 | }
31 | }
32 |
33 | func (c *Client) Connect(addr string) error {
34 | serverAddr, err := net.ResolveUDPAddr("udp", addr)
35 | if err != nil {
36 | fmt.Println("Error resolving server udp address: ", err)
37 | return err
38 | }
39 |
40 | c.conn, err = net.DialUDP("udp", nil, serverAddr)
41 | if err != nil {
42 | fmt.Println("Error connecting to udp server address: ", err)
43 | return err
44 | }
45 |
46 | data := make([]byte, 65500)
47 | go func() {
48 | for c.conn != nil {
49 | n, _, err := c.conn.ReadFromUDP(data)
50 | if err != nil {
51 | fmt.Println("Error reading udp packet: ", err)
52 | continue
53 | }
54 |
55 | dataBuf := bytes.NewBuffer(data[0:n])
56 | gzipReader, err := gzip.NewReader(dataBuf)
57 | if err != nil {
58 | fmt.Println("Error creating gzip Reader for udp packet: ", err)
59 | continue
60 | }
61 |
62 | unzipped, err := ioutil.ReadAll(gzipReader)
63 | if err != nil {
64 | fmt.Println("Error unzipping udp packet: ", err)
65 | continue
66 | }
67 |
68 | var packet Packet
69 | for i := 0; i < len(unzipped); {
70 | j := i
71 | packet, err, i = Decode(unzipped, i)
72 | if err != nil {
73 | fmt.Println("Error decoding udp packet: ", err)
74 | continue
75 | }
76 | c.updateBytesReceived(packet.Command, int64(i-j))
77 |
78 | c.token = packet.Token
79 |
80 | if c.onPacketReceived != nil {
81 | c.onPacketReceived(packet)
82 | }
83 | }
84 | }
85 | }()
86 | return nil
87 | }
88 |
89 | func (c *Client) PacketReceived(callback func(packet Packet)) {
90 | c.onPacketReceived = callback
91 | }
92 |
93 | func (c *Client) WriteMessage(command string, data []byte) {
94 | packet := Packet{
95 | Token: c.token,
96 | Command: command,
97 | Data: data,
98 | }
99 |
100 | packetData := Encode(packet)
101 | var gzipBuf bytes.Buffer
102 | gzipWriter := gzip.NewWriter(&gzipBuf)
103 | _, err := gzipWriter.Write(packetData)
104 | if err != nil {
105 | fmt.Println("Error Gzip compressing udp message: ", err)
106 | return
107 | }
108 |
109 | if err := gzipWriter.Flush(); err != nil {
110 | fmt.Println("Error Flushing Gzip writer for udp message: ", err)
111 | return
112 | }
113 |
114 | if err := gzipWriter.Close(); err != nil {
115 | fmt.Println("Error Closing Gzip writer for udp message: ", err)
116 | return
117 | }
118 |
119 | gzipData := gzipBuf.Bytes()
120 | c.updateBytesSent(command, int64(len(gzipData)))
121 | _, err = c.conn.Write(gzipData)
122 | if err != nil {
123 | fmt.Println("Error writing udp message: ", err)
124 | }
125 | }
126 |
127 | func (c *Client) Close() {
128 | c.conn.Close()
129 | }
130 |
131 | func (c *Client) updateBytesSent(event string, sent int64) {
132 | c.bytesByEventMux.Lock()
133 | c.bytesSent += sent
134 | total, ok := c.bytesSentByEvent[event]
135 | if !ok {
136 | c.bytesSentByEvent[event], total = 0, 0
137 | }
138 | c.bytesSentByEvent[event] = sent + total
139 | c.bytesByEventMux.Unlock()
140 | }
141 |
142 | func (c *Client) updateBytesReceived(event string, sent int64) {
143 | c.bytesByEventMux.Lock()
144 | c.bytesReceived += sent
145 | total, ok := c.bytesReceivedByEvent[event]
146 | if !ok {
147 | c.bytesReceivedByEvent[event], total = 0, 0
148 | }
149 | c.bytesReceivedByEvent[event] = sent + total
150 | c.bytesByEventMux.Unlock()
151 | }
152 |
153 | func (c *Client) GetBytesSentByEvent() (byEvent map[string]int64) {
154 | c.bytesByEventMux.Lock()
155 | byEvent = make(map[string]int64)
156 | for k, v := range c.bytesSentByEvent {
157 | byEvent[k] = v
158 | }
159 | c.bytesByEventMux.Unlock()
160 | return byEvent
161 | }
162 |
163 | func (c *Client) GetBytesReceivedByEvent() (byEvent map[string]int64) {
164 | c.bytesByEventMux.Lock()
165 | byEvent = make(map[string]int64)
166 | for k, v := range c.bytesReceivedByEvent {
167 | byEvent[k] = v
168 | }
169 | c.bytesByEventMux.Unlock()
170 | return byEvent
171 | }
172 |
--------------------------------------------------------------------------------
/networking/clock.go:
--------------------------------------------------------------------------------
1 | package networking
2 |
3 | //TODO
4 |
--------------------------------------------------------------------------------
/networking/packet.go:
--------------------------------------------------------------------------------
1 | package networking
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "fmt"
7 | )
8 |
9 | type Packet struct {
10 | Token string
11 | Command string
12 | Data []byte
13 | }
14 |
15 | func Encode(packet Packet) []byte {
16 | tokenLen := len(packet.Token)
17 | commandLen := len(packet.Command)
18 | dataLen := len(packet.Data)
19 | data := new(bytes.Buffer)
20 | data.WriteByte(byte(tokenLen))
21 | data.WriteByte(byte(commandLen))
22 | binary.Write(data, binary.LittleEndian, uint16(dataLen))
23 | data.WriteString(packet.Token)
24 | data.WriteString(packet.Command)
25 | data.Write(packet.Data)
26 | return data.Bytes()
27 | }
28 |
29 | func Decode(data []byte, i int) (Packet, error, int) {
30 | if len(data)-i < 4 {
31 | return Packet{}, fmt.Errorf("No data provided to Decode: len=%v", len(data)), len(data)
32 | }
33 | tokenLen := int(data[i])
34 | commandLen := int(data[i+1])
35 | dataLen := int(binary.LittleEndian.Uint16(data[i+2 : i+4]))
36 | i += 4
37 | token := string(data[i : i+tokenLen])
38 | i += tokenLen
39 | command := string(data[i : i+commandLen])
40 | i += commandLen
41 | packetData := data[i : i+dataLen]
42 | i += dataLen
43 | return Packet{
44 | Token: token,
45 | Command: command,
46 | Data: packetData,
47 | }, nil, i
48 | }
49 |
--------------------------------------------------------------------------------
/networking/packet_test.go:
--------------------------------------------------------------------------------
1 | package networking
2 |
3 | import (
4 | "encoding/binary"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestEncode(t *testing.T) {
11 | packet := Packet{
12 | Token: "123",
13 | Command: "testCommand",
14 | Data: []byte("test Data"),
15 | }
16 | var bytes [2]byte
17 | binary.LittleEndian.PutUint16(bytes[:], uint16(len(packet.Data)))
18 | expectedData := []byte{byte(len(packet.Token)), byte(len(packet.Command)), bytes[0], bytes[1]}
19 | expectedData = append(expectedData, []byte(packet.Token)...)
20 | expectedData = append(expectedData, []byte(packet.Command)...)
21 | expectedData = append(expectedData, packet.Data...)
22 |
23 | data := Encode(packet)
24 | assert.EqualValues(t, expectedData, data, "Encode packet didn't work")
25 | }
26 |
27 | func TestDecode(t *testing.T) {
28 | expectedPacket := Packet{
29 | Token: "123",
30 | Command: "testCommand",
31 | Data: []byte("test Data"),
32 | }
33 | data := Encode(expectedPacket)
34 |
35 | packet, err, i := Decode(data, 0)
36 | assert.Nil(t, err, "decode should not return an error")
37 | assert.EqualValues(t, len(data), i, "Decode should return the correct read index")
38 | assert.EqualValues(t, expectedPacket, packet, "Decode packet didn't work")
39 | }
40 |
41 | func TestDecodeMultiple(t *testing.T) {
42 | testPacket1 := Packet{
43 | Token: "123",
44 | Command: "testCommand",
45 | Data: []byte("test Data"),
46 | }
47 | testPacket2 := Packet{
48 | Token: "12345678",
49 | Command: "cmdTest",
50 | Data: []byte("123 4567 abcd"),
51 | }
52 |
53 | data := append(Encode(testPacket1), Encode(testPacket2)...)
54 |
55 | packet, err, i := Decode(data, 0)
56 | assert.Nil(t, err, "decode should not return an error")
57 | assert.EqualValues(t, testPacket1, packet, "Decode first packet didn't work")
58 |
59 | packet, err, i = Decode(data, i)
60 | assert.EqualValues(t, len(data), i, "Decode should return the correct read index")
61 | assert.EqualValues(t, testPacket2, packet, "Decode second packet didn't work")
62 | }
63 |
--------------------------------------------------------------------------------
/networking/session.go:
--------------------------------------------------------------------------------
1 | package networking
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "net"
7 | "time"
8 | )
9 |
10 | var tokens int64
11 |
12 | type Session struct {
13 | token string
14 | addr *net.UDPAddr
15 | idleTimer time.Time
16 | packetBuffer *bytes.Buffer
17 | }
18 |
19 | func NewSession(addr *net.UDPAddr) *Session {
20 | return &Session{
21 | token: generateToken(),
22 | addr: addr,
23 | idleTimer: time.Now(),
24 | packetBuffer: new(bytes.Buffer),
25 | }
26 | }
27 |
28 | func generateToken() string {
29 | tokens++
30 | return fmt.Sprintf("%v", tokens)
31 | }
32 |
--------------------------------------------------------------------------------
/opengl/shaders.go:
--------------------------------------------------------------------------------
1 | package opengl
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "strings"
7 |
8 | "github.com/go-gl/gl/v4.1-core/gl"
9 | "github.com/go-gl/mathgl/mgl32"
10 | "github.com/walesey/go-engine/renderer"
11 | )
12 |
13 | func newProgram(shaders ...uint32) (uint32, error) {
14 | program := gl.CreateProgram()
15 |
16 | for _, shader := range shaders {
17 | gl.AttachShader(program, shader)
18 | }
19 | gl.LinkProgram(program)
20 |
21 | var status int32
22 | gl.GetProgramiv(program, gl.LINK_STATUS, &status)
23 | if status == gl.FALSE {
24 | var logLength int32
25 | gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &logLength)
26 |
27 | log := strings.Repeat("\x00", int(logLength+1))
28 | gl.GetProgramInfoLog(program, logLength, nil, gl.Str(log))
29 |
30 | return 0, errors.New(fmt.Sprintf("failed to link program: %v", log))
31 | }
32 |
33 | for _, shader := range shaders {
34 | gl.DeleteShader(shader)
35 | }
36 |
37 | return program, nil
38 | }
39 |
40 | func compileShader(source string, shaderType uint32) (uint32, error) {
41 | shader := gl.CreateShader(shaderType)
42 | csource, free := gl.Strs(source)
43 | gl.ShaderSource(shader, 1, csource, nil)
44 | free()
45 | gl.CompileShader(shader)
46 |
47 | var status int32
48 | gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
49 | if status == gl.FALSE {
50 | var logLength int32
51 | gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
52 |
53 | log := strings.Repeat("\x00", int(logLength+1))
54 | gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
55 |
56 | return 0, fmt.Errorf("failed to compile %v: %v", source, log)
57 | }
58 |
59 | return shader, nil
60 | }
61 |
62 | func setupUniforms(shader *renderer.Shader) {
63 | for name, uniform := range shader.Uniforms {
64 | uniformLocation := gl.GetUniformLocation(shader.Program, gl.Str(name+"\x00"))
65 | switch t := uniform.(type) {
66 | case bool:
67 | if t {
68 | gl.Uniform1i(uniformLocation, 1)
69 | } else {
70 | gl.Uniform1i(uniformLocation, 0)
71 | }
72 | case float32:
73 | gl.Uniform1f(uniformLocation, t)
74 | case float64:
75 | gl.Uniform1f(uniformLocation, float32(t))
76 | case int32:
77 | gl.Uniform1i(uniformLocation, t)
78 | case int:
79 | gl.Uniform1i(uniformLocation, int32(t))
80 | case mgl32.Vec2:
81 | gl.Uniform2f(uniformLocation, t[0], t[1])
82 | case mgl32.Vec3:
83 | gl.Uniform3f(uniformLocation, t[0], t[1], t[2])
84 | case mgl32.Vec4:
85 | gl.Uniform4f(uniformLocation, t[0], t[1], t[2], t[3])
86 | case mgl32.Mat4:
87 | gl.UniformMatrix4fv(uniformLocation, 1, false, &t[0])
88 | case []float32:
89 | gl.Uniform4fv(uniformLocation, (int32)(len(t)), &t[0])
90 | case []int32:
91 | gl.Uniform4iv(uniformLocation, (int32)(len(t)), &t[0])
92 | default:
93 | fmt.Printf("unexpected type for shader uniform: %T\n", t)
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/physics/chipmunk/body.go:
--------------------------------------------------------------------------------
1 | package chipmunkPhysics
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/vova616/chipmunk"
6 | "github.com/vova616/chipmunk/vect"
7 | )
8 |
9 | type ChipmunkBody struct {
10 | Body *chipmunk.Body
11 | }
12 |
13 | func NewChipmunkBody(mass, i float32) *ChipmunkBody {
14 | return &ChipmunkBody{
15 | Body: chipmunk.NewBody(vect.Float(mass), vect.Float(i)),
16 | }
17 | }
18 |
19 | func NewChipmunkBodyStatic() *ChipmunkBody {
20 | return &ChipmunkBody{
21 | Body: chipmunk.NewBodyStatic(),
22 | }
23 | }
24 |
25 | func (cBody *ChipmunkBody) KineticEnergy() float32 {
26 | return float32(cBody.Body.KineticEnergy())
27 | }
28 |
29 | func (cBody *ChipmunkBody) SetMass(mass float32) {
30 | cBody.Body.SetMass(vect.Float(mass))
31 | }
32 |
33 | func (cBody *ChipmunkBody) SetMoment(moment float32) {
34 | cBody.Body.SetMoment(vect.Float(moment))
35 | }
36 |
37 | func (cBody *ChipmunkBody) GetMoment() float32 {
38 | return cBody.Body.Moment()
39 | }
40 |
41 | func (cBody *ChipmunkBody) SetAngle(angle float32) {
42 | cBody.Body.SetAngle(vect.Float(angle))
43 | }
44 |
45 | func (cBody *ChipmunkBody) AddAngle(angle float32) {
46 | cBody.Body.AddAngle(angle)
47 | }
48 |
49 | func (cBody *ChipmunkBody) GetMass() float32 {
50 | return float32(cBody.Body.Mass())
51 | }
52 |
53 | func (cBody *ChipmunkBody) SetPosition(pos mgl32.Vec2) {
54 | cBody.Body.SetPosition(convertToVect(pos))
55 | }
56 |
57 | func (cBody *ChipmunkBody) AddForce(force mgl32.Vec2) {
58 | cBody.Body.AddForce(force.X(), force.Y())
59 | }
60 |
61 | func (cBody *ChipmunkBody) SetForce(force mgl32.Vec2) {
62 | cBody.Body.SetForce(force.X(), force.Y())
63 | }
64 |
65 | func (cBody *ChipmunkBody) AddVelocity(velocity mgl32.Vec2) {
66 | cBody.Body.AddVelocity(velocity.X(), velocity.Y())
67 | }
68 |
69 | func (cBody *ChipmunkBody) SetVelocity(velocity mgl32.Vec2) {
70 | cBody.Body.SetVelocity(velocity.X(), velocity.Y())
71 | }
72 |
73 | func (cBody *ChipmunkBody) AddTorque(t float32) {
74 | cBody.Body.AddTorque(float32(t))
75 | }
76 |
77 | func (cBody *ChipmunkBody) GetTorque() float32 {
78 | return cBody.Body.Torque()
79 | }
80 |
81 | func (cBody *ChipmunkBody) GetAngularVelocity() float32 {
82 | return cBody.Body.AngularVelocity()
83 | }
84 |
85 | func (cBody *ChipmunkBody) SetTorque(t float32) {
86 | cBody.Body.SetTorque(float32(t))
87 | }
88 |
89 | func (cBody *ChipmunkBody) AddAngularVelocity(w float32) {
90 | cBody.Body.AddAngularVelocity(w)
91 | }
92 |
93 | func (cBody *ChipmunkBody) SetAngularVelocity(w float32) {
94 | cBody.Body.SetAngularVelocity(w)
95 | }
96 |
97 | func (cBody *ChipmunkBody) GetVelocity() mgl32.Vec2 {
98 | return convertFromVect(cBody.Body.Velocity())
99 | }
100 |
101 | func (cBody *ChipmunkBody) GetPosition() mgl32.Vec2 {
102 | return convertFromVect(cBody.Body.Position())
103 | }
104 |
105 | func (cBody *ChipmunkBody) GetAngle() float32 {
106 | return float32(cBody.Body.Angle())
107 | }
108 |
--------------------------------------------------------------------------------
/physics/chipmunk/world.go:
--------------------------------------------------------------------------------
1 | package chipmunkPhysics
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/vova616/chipmunk"
6 | "github.com/vova616/chipmunk/vect"
7 | "github.com/walesey/go-engine/physics/physicsAPI"
8 | )
9 |
10 | type ChipmonkSpace struct {
11 | Space *chipmunk.Space
12 | OnCollision func(shapeA, shapeB *chipmunk.Shape)
13 | }
14 |
15 | func NewChipmonkSpace() *ChipmonkSpace {
16 | return &ChipmonkSpace{
17 | Space: chipmunk.NewSpace(),
18 | }
19 | }
20 |
21 | func (cSpace *ChipmonkSpace) Update(dt float64) {
22 | cSpace.Space.Step(vect.Float(dt))
23 | if cSpace.OnCollision != nil {
24 | for _, a := range cSpace.Space.Arbiters {
25 | cSpace.OnCollision(a.ShapeA, a.ShapeB)
26 | }
27 | }
28 | }
29 |
30 | func (cSpace *ChipmonkSpace) SetOnCollision(onCollision func(shapeA, shapeB *chipmunk.Shape)) {
31 | cSpace.OnCollision = onCollision
32 | }
33 |
34 | func (cSpace *ChipmonkSpace) AddBody(body physicsAPI.PhysicsObject2D) {
35 | cBody, ok := body.(*ChipmunkBody)
36 | if ok {
37 | cSpace.Space.AddBody(cBody.Body)
38 | }
39 | }
40 |
41 | func (cSpace *ChipmonkSpace) RemoveBody(body physicsAPI.PhysicsObject2D) {
42 | cBody, ok := body.(*ChipmunkBody)
43 | if ok {
44 | cSpace.Space.RemoveBody(cBody.Body)
45 | }
46 | }
47 |
48 | func (cSpace *ChipmonkSpace) SetGravity(gravity mgl32.Vec2) {
49 | cSpace.Space.Gravity = convertToVect(gravity)
50 | }
51 |
52 | func (cSpace *ChipmonkSpace) GetGravity() mgl32.Vec2 {
53 | return convertFromVect(cSpace.Space.Gravity)
54 | }
55 |
56 | func convertFromVect(v vect.Vect) mgl32.Vec2 {
57 | return mgl32.Vec2{float32(v.X), float32(v.Y)}
58 | }
59 |
60 | func convertToVect(v mgl32.Vec2) vect.Vect {
61 | return vect.Vect{X: vect.Float(v.X()), Y: vect.Float(v.Y())}
62 | }
63 |
--------------------------------------------------------------------------------
/physics/physicsAPI/characterController.go:
--------------------------------------------------------------------------------
1 | package physicsAPI
2 |
3 | import "github.com/go-gl/mathgl/mgl32"
4 |
5 | type CharacterController interface {
6 | Delete()
7 | Warp(position mgl32.Vec3)
8 | Jump()
9 | SetWalkDirection(dir mgl32.Vec3)
10 | SetVelocityForTimeInterval(speed mgl32.Vec3, time float32)
11 |
12 | SetUpAxis(axis int)
13 | SetFallSpeed(speed float32)
14 | SetJumpSpeed(speed float32)
15 | SetMaxJumpHeight(height float32)
16 | SetGravity(gravity float32)
17 | SetMaxSlope(radian float32)
18 |
19 | GetPosition() mgl32.Vec3
20 | CanJump() bool
21 | GetGravity() float32
22 | GetMaxSlope() float32
23 | OnGround() bool
24 | }
25 |
--------------------------------------------------------------------------------
/physics/physicsAPI/constraints.go:
--------------------------------------------------------------------------------
1 | package physicsAPI
2 |
3 | type ConstraintSolver interface {
4 | SolveGroup(stepTime float32, constraints *[]Constraint)
5 | }
6 |
7 | type Constraint interface{}
8 |
--------------------------------------------------------------------------------
/physics/physicsAPI/physicsObject.go:
--------------------------------------------------------------------------------
1 | package physicsAPI
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | )
6 |
7 | type PhysicsObject interface {
8 | Delete()
9 | ApplyForce(force, position mgl32.Vec3)
10 | ApplyTorque(torque mgl32.Vec3)
11 |
12 | GetPosition() mgl32.Vec3
13 | GetVelocity() mgl32.Vec3
14 | GetOrientation() mgl32.Quat
15 | GetAngularVelocityVector() mgl32.Vec3
16 | GetMass() float32
17 | GetRadius() float32
18 | GetFriction() float32
19 | GetRestitution() float32
20 | InertiaTensor() mgl32.Mat3
21 | IsStatic() bool
22 |
23 | SetPosition(position mgl32.Vec3)
24 | SetVelocity(velocity mgl32.Vec3)
25 | SetOrientation(orientation mgl32.Quat)
26 | SetAngularVelocityVector(av mgl32.Vec3)
27 | SetMass(mass float32)
28 | SetRadius(radius float32)
29 | SetFriction(friction float32)
30 | SetRestitution(restitution float32)
31 | }
32 |
33 | type PhysicsObject2D interface {
34 | KineticEnergy() float32
35 | SetMass(mass float32)
36 | SetMoment(moment float32)
37 | GetMoment() float32
38 | SetAngle(angle float32)
39 | AddAngle(angle float32)
40 | GetMass() float32
41 | SetPosition(pos mgl32.Vec2)
42 | AddForce(force mgl32.Vec2)
43 | SetForce(force mgl32.Vec2)
44 | AddVelocity(velocity mgl32.Vec2)
45 | SetVelocity(velocity mgl32.Vec2)
46 | AddTorque(t float32)
47 | GetTorque() float32
48 | GetAngularVelocity() float32
49 | SetTorque(t float32)
50 | AddAngularVelocity(w float32)
51 | SetAngularVelocity(w float32)
52 | GetVelocity() mgl32.Vec2
53 | GetPosition() mgl32.Vec2
54 | GetAngle() float32
55 | }
56 |
--------------------------------------------------------------------------------
/physics/physicsAPI/physicsSpace.go:
--------------------------------------------------------------------------------
1 | package physicsAPI
2 |
3 | import "github.com/go-gl/mathgl/mgl32"
4 |
5 | type PhysicsSpace interface {
6 | Update(dt float64)
7 | SimulateStep(stepTime float32, subSteps int)
8 | Delete()
9 | AddObject(objects ...PhysicsObject)
10 | RemoveObject(objects ...PhysicsObject)
11 | AddCharacterController(characterController CharacterController)
12 | SetConstraintSolver(solver ConstraintSolver)
13 | AddConstraint(constraint Constraint)
14 | RemoveConstraints(constraint ...Constraint)
15 | SetGravity(gravity mgl32.Vec3)
16 | GetGravity() mgl32.Vec3
17 | }
18 |
19 | type PhysicsSpace2D interface {
20 | Update(dt float64)
21 | AddBody(body PhysicsObject2D)
22 | RemoveBody(body PhysicsObject2D)
23 | SetGravity(gravity mgl32.Vec2)
24 | GetGravity() mgl32.Vec2
25 | }
26 |
--------------------------------------------------------------------------------
/renderer/Cubemap.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | import (
4 | "image"
5 |
6 | "github.com/disintegration/imaging"
7 | )
8 |
9 | type CubeMap struct {
10 | Id uint32
11 | Lod bool
12 | Loaded bool
13 |
14 | Right, Left, Top, Bottom, Back, Front image.Image
15 | }
16 |
17 | func NewCubemap(baseImage image.Image, lod bool) *CubeMap {
18 | cubeMap := new(CubeMap)
19 |
20 | x := baseImage.Bounds().Max.X
21 | y := baseImage.Bounds().Max.Y
22 |
23 | cubeMap.Lod = lod
24 | cubeMap.Right = imaging.Crop(baseImage, image.Rect(x/2, y/3, 3*x/4, 2*y/3))
25 | cubeMap.Left = imaging.Crop(baseImage, image.Rect(0, y/3, x/4, 2*y/3))
26 | cubeMap.Top = imaging.Crop(baseImage, image.Rect(x/4, 0, x/2, y/3))
27 | cubeMap.Bottom = imaging.Crop(baseImage, image.Rect(x/4, 2*y/3, x/2, y))
28 | cubeMap.Back = imaging.Crop(baseImage, image.Rect(3*x/4, y/3, x, 2*y/3))
29 | cubeMap.Front = imaging.Crop(baseImage, image.Rect(x/4, y/3, x/2, 2*y/3))
30 |
31 | return cubeMap
32 | }
33 |
34 | func (cm *CubeMap) Destroy(renderer Renderer) {
35 | if cm != nil {
36 | renderer.DestroyCubeMap(cm)
37 | cm.Loaded = false
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/renderer/camera.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/go-gl/mathgl/mgl32"
7 | "github.com/walesey/go-engine/util"
8 | )
9 |
10 | // The camera Entity
11 | type Camera struct {
12 | Translation, Lookat, Up mgl32.Vec3
13 | Angle, Near, Far float32
14 | Ortho bool
15 | }
16 |
17 | func CreateCamera() *Camera {
18 | cam := Camera{
19 | Translation: mgl32.Vec3{0, 0, 0},
20 | Lookat: mgl32.Vec3{1, 0, 0},
21 | Up: mgl32.Vec3{0, 1, 0},
22 | Angle: 45.0,
23 | Near: 0.1,
24 | Far: 999999999.0,
25 | }
26 |
27 | return &cam
28 | }
29 |
30 | func (c *Camera) GetDirection() mgl32.Vec3 {
31 | return c.Lookat.Sub(c.Translation).Normalize()
32 | }
33 |
34 | // GetMouseVector - Returns a normal vector given by the mouse position
35 | func (c *Camera) GetMouseVector(windowSize, mouse mgl32.Vec2) mgl32.Vec3 {
36 | v, err := mgl32.UnProject(
37 | mgl32.Vec3{mouse.X(), windowSize.Y() - mouse.Y(), 0.5},
38 | mgl32.LookAtV(c.Translation, c.Lookat, c.Up),
39 | mgl32.Perspective(mgl32.DegToRad(c.Angle), windowSize.X()/windowSize.Y(), c.Near, c.Far),
40 | 0, 0, int(windowSize.X()), int(windowSize.Y()),
41 | )
42 | if err != nil {
43 | log.Println("Error converting camera vector: ", err)
44 | return c.Lookat
45 | }
46 |
47 | return v.Sub(c.Translation).Normalize()
48 | }
49 |
50 | // GetWindowVector - Returns the screen position of the given world vector
51 | func (c *Camera) GetWindowVector(windowSize mgl32.Vec2, point mgl32.Vec3) mgl32.Vec3 {
52 | v := mgl32.Project(
53 | point,
54 | mgl32.LookAtV(c.Translation, c.Lookat, c.Up),
55 | mgl32.Perspective(mgl32.DegToRad(c.Angle), windowSize.X()/windowSize.Y(), c.Near, c.Far),
56 | 0, 0, int(windowSize.X()), int(windowSize.Y()),
57 | )
58 |
59 | return mgl32.Vec3{v.X(), windowSize.Y() - v.Y(), v.Z()}
60 | }
61 |
62 | // CameraContainsSphere - determines if a sphere in contained in the frustrum given by the camera.
63 | // sphere is given by point and radius
64 | func (c *Camera) CameraContainsSphere(windowSize mgl32.Vec2, radius float32, point mgl32.Vec3) bool {
65 | return c.FrustrumContainsSphere(windowSize, mgl32.Vec2{}, windowSize, radius, point)
66 | }
67 |
68 | // FrustrumContainsSphere - determines if a sphere in contained in the frustrum given by start/end vectors on the screen.
69 | // sphere is given by point and radius
70 | func (c *Camera) FrustrumContainsSphere(windowSize, start, end mgl32.Vec2, radius float32, point mgl32.Vec3) bool {
71 | tlv := c.GetMouseVector(windowSize, start)
72 | trv := c.GetMouseVector(windowSize, mgl32.Vec2{end.X(), start.Y()})
73 | blv := c.GetMouseVector(windowSize, mgl32.Vec2{start.X(), end.Y()})
74 | brv := c.GetMouseVector(windowSize, end)
75 |
76 | r := radius
77 | delta := point.Sub(c.Translation)
78 | if point.ApproxEqual(c.Translation) {
79 | delta = mgl32.Vec3{1, 0, 0}
80 | }
81 |
82 | return delta.Dot(tlv.Cross(trv).Normalize()) > -r &&
83 | delta.Dot(trv.Cross(brv).Normalize()) > -r &&
84 | delta.Dot(brv.Cross(blv).Normalize()) > -r &&
85 | delta.Dot(blv.Cross(tlv).Normalize()) > -r &&
86 | util.Vec3LenSq(delta) < (c.Far+r)*(c.Far+r)
87 | }
88 |
89 | func (c *Camera) SetScale(scale mgl32.Vec3) {} //na
90 |
91 | func (c *Camera) SetTranslation(translation mgl32.Vec3) {
92 | c.Translation = translation
93 | }
94 |
95 | func (c *Camera) SetOrientation(orientation mgl32.Quat) {
96 | direction := orientation.Rotate(mgl32.Vec3{1, 0, 0})
97 | c.Lookat = c.Translation.Add(direction)
98 | }
99 |
--------------------------------------------------------------------------------
/renderer/fpsMeter.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type FPSMeter struct {
8 | start time.Time
9 | last time.Time
10 | frames int
11 | sampleTime float64
12 | FpsCap float64
13 | FrameTime float64
14 | value float64
15 | }
16 |
17 | func CreateFPSMeter(sampleTime float64) *FPSMeter {
18 | return &FPSMeter{start: time.Now(), last: time.Now(), frames: 0, sampleTime: sampleTime, FpsCap: 120}
19 | }
20 |
21 | func (fps *FPSMeter) UpdateFPSMeter() float64 {
22 | fps.frames = fps.frames + 1
23 | elapsed := time.Since(fps.start)
24 | if elapsed.Seconds() >= fps.sampleTime {
25 | fps.value = (float64)(fps.frames) / fps.sampleTime
26 | fps.start = time.Now()
27 | fps.frames = 0
28 | }
29 |
30 | fps.FrameTime = time.Since(fps.last).Seconds()
31 | sleepTime := (time.Duration)((1000.0 / fps.FpsCap) - (1000.0 * fps.FrameTime))
32 | if sleepTime > 0 {
33 | time.Sleep(sleepTime * time.Millisecond)
34 | }
35 | frameTime := time.Since(fps.last)
36 | fps.last = time.Now()
37 | return frameTime.Seconds()
38 | }
39 |
40 | func (fps *FPSMeter) Value() float64 {
41 | return fps.value
42 | }
43 |
--------------------------------------------------------------------------------
/renderer/light.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | import "github.com/go-gl/mathgl/mgl32"
4 |
5 | type LightType int
6 |
7 | const (
8 | _ LightType = iota
9 | POINT
10 | DIRECTIONAL
11 | AMBIENT
12 | )
13 |
14 | type Light struct {
15 | LightType
16 | Color [3]float32 //RGB
17 | Position mgl32.Vec3
18 | Direction mgl32.Vec3
19 | }
20 |
21 | func NewLight(lightType LightType) *Light {
22 | return &Light{
23 | LightType: lightType,
24 | Color: [3]float32{1, 1, 1},
25 | Direction: mgl32.Vec3{1, 0, 0},
26 | }
27 | }
28 |
29 | func (l *Light) SetScale(scale mgl32.Vec3) {} //na
30 |
31 | func (l *Light) SetTranslation(translation mgl32.Vec3) {
32 | if l.LightType == POINT {
33 | l.Position = translation
34 | }
35 | }
36 |
37 | func (l *Light) SetOrientation(orientation mgl32.Quat) {
38 | if l.LightType == DIRECTIONAL {
39 | l.Direction = orientation.Rotate(mgl32.Vec3{1, 0, 0})
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/renderer/material.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | import (
4 | "image"
5 | )
6 |
7 | type Texture struct {
8 | TextureId uint32
9 | TextureName string
10 | Img image.Image
11 | Lod bool
12 | Loaded bool
13 | }
14 |
15 | type Material struct {
16 | Textures []*Texture
17 | }
18 |
19 | func NewTexture(name string, img image.Image, lod bool) *Texture {
20 | return &Texture{
21 | TextureName: name,
22 | Img: img,
23 | Lod: lod,
24 | }
25 | }
26 |
27 | func NewMaterial(textures ...*Texture) *Material {
28 | return &Material{
29 | Textures: textures,
30 | }
31 | }
32 |
33 | func (m *Material) Destroy(renderer Renderer) {
34 | if m != nil {
35 | renderer.DestroyMaterial(m)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/renderer/primitiveData.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | var cubeIndicies = []uint32{
4 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
5 | 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
6 | }
7 |
8 | var skyboxVerticies = []float32{
9 | -1, -1, -1, 1, 1, 1, 0.25, 0.334, 1, 1, 1, 1,
10 | -1, 1, -1, 1, 1, 1, 0.25, 0.6662, 1, 1, 1, 1,
11 | -1, 1, 1, 1, 1, 1, 0.5, 0.6662, 1, 1, 1, 1,
12 | -1, 1, 1, 1, 1, 1, 0.5, 0.6662, 1, 1, 1, 1,
13 | -1, -1, 1, 1, 1, 1, 0.5, 0.334, 1, 1, 1, 1,
14 | -1, -1, -1, 1, 1, 1, 0.25, 0.334, 1, 1, 1, 1,
15 | 1, -1, -1, 1, 1, 1, 1, 0.334, 1, 1, 1, 1,
16 | 1, -1, 1, 1, 1, 1, 0.75, 0.334, 1, 1, 1, 1,
17 | 1, 1, 1, 1, 1, 1, 0.75, 0.666, 1, 1, 1, 1,
18 | 1, 1, 1, 1, 1, 1, 0.75, 0.666, 1, 1, 1, 1,
19 | 1, 1, -1, 1, 1, 1, 1, 0.666, 1, 1, 1, 1,
20 | 1, -1, -1, 1, 1, 1, 1, 0.334, 1, 1, 1, 1,
21 | -1, -1, -1, 1, 1, 1, 0.25, 0.333, 1, 1, 1, 1,
22 | -1, -1, 1, 1, 1, 1, 0.5, 0.333, 1, 1, 1, 1,
23 | 1, -1, 1, 1, 1, 1, 0.5, 0, 1, 1, 1, 1,
24 | 1, -1, 1, 1, 1, 1, 0.5, 0, 1, 1, 1, 1,
25 | 1, -1, -1, 1, 1, 1, 0.25, 0, 1, 1, 1, 1,
26 | -1, -1, -1, 1, 1, 1, 0.25, 0.333, 1, 1, 1, 1,
27 | -1, -1, 1, 1, 1, 1, 0.5, 0.334, 1, 1, 1, 1,
28 | -1, 1, 1, 1, 1, 1, 0.5, 0.666, 1, 1, 1, 1,
29 | 1, 1, 1, 1, 1, 1, 0.75, 0.666, 1, 1, 1, 1,
30 | 1, 1, 1, 1, 1, 1, 0.75, 0.666, 1, 1, 1, 1,
31 | 1, -1, 1, 1, 1, 1, 0.75, 0.334, 1, 1, 1, 1,
32 | -1, -1, 1, 1, 1, 1, 0.5, 0.334, 1, 1, 1, 1,
33 | -1, 1, 1, 1, 1, 1, 0.5, 0.667, 1, 1, 1, 1,
34 | -1, 1, -1, 1, 1, 1, 0.25, 0.667, 1, 1, 1, 1,
35 | 1, 1, -1, 1, 1, 1, 0.25, 1, 1, 1, 1, 1,
36 | 1, 1, -1, 1, 1, 1, 0.25, 1, 1, 1, 1, 1,
37 | 1, 1, 1, 1, 1, 1, 0.5, 1, 1, 1, 1, 1,
38 | -1, 1, 1, 1, 1, 1, 0.5, 0.667, 1, 1, 1, 1,
39 | 1, -1, -1, 1, 1, 1, 0, 0.333, 1, 1, 1, 1,
40 | 1, 1, -1, 1, 1, 1, 0, 0.666, 1, 1, 1, 1,
41 | -1, 1, -1, 1, 1, 1, 0.25, 0.666, 1, 1, 1, 1,
42 | -1, 1, -1, 1, 1, 1, 0.25, 0.666, 1, 1, 1, 1,
43 | -1, -1, -1, 1, 1, 1, 0.25, 0.333, 1, 1, 1, 1,
44 | 1, -1, -1, 1, 1, 1, 0, 0.333, 1, 1, 1, 1,
45 | }
46 |
--------------------------------------------------------------------------------
/renderer/renderer.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | import "github.com/go-gl/mathgl/mgl32"
4 |
5 | type Renderer interface {
6 | SetInit(callback func())
7 | SetUpdate(callback func())
8 | SetRender(callback func())
9 | SetCamera(camera *Camera)
10 | Camera() *Camera
11 | Start()
12 |
13 | BackGroundColor(r, g, b, a float32)
14 | WindowDimensions() mgl32.Vec2
15 | LockCursor(lock bool)
16 | UseRendererParams(params RendererParams)
17 |
18 | DrawGeometry(geometry *Geometry, transform mgl32.Mat4)
19 | DestroyGeometry(geometry *Geometry)
20 |
21 | UseMaterial(material *Material)
22 | DestroyMaterial(material *Material)
23 |
24 | UseCubeMap(cubeMap *CubeMap)
25 | DestroyCubeMap(cubeMap *CubeMap)
26 |
27 | UseShader(shader *Shader)
28 |
29 | CreatePostEffect(shader *Shader)
30 | DestroyPostEffects(shader *Shader)
31 |
32 | AddLight(light *Light)
33 | RemoveLight(light *Light)
34 | }
35 |
36 | //A Spatial is something that can be Added to scenegraph nodes
37 | type Spatial interface {
38 | Draw(renderer Renderer, transform mgl32.Mat4)
39 | Optimize(geometry *Geometry, transform mgl32.Mat4)
40 | Destroy(renderer Renderer)
41 | Center() mgl32.Vec3
42 | BoundingRadius() float32
43 | OrthoOrder() int
44 | SetParent(parent *Node)
45 | }
46 |
47 | //An Entity is something that can be scaled, positioned and rotated (orientation)
48 | type Entity interface {
49 | SetScale(scale mgl32.Vec3)
50 | SetTranslation(translation mgl32.Vec3)
51 | SetOrientation(orientation mgl32.Quat)
52 | }
53 |
--------------------------------------------------------------------------------
/renderer/sceneGraph.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | import (
4 | "sort"
5 |
6 | "github.com/go-gl/mathgl/mgl32"
7 | "github.com/go-gl/mathgl/mgl32/matstack"
8 | "github.com/walesey/go-engine/util"
9 | )
10 |
11 | type SceneGraph struct {
12 | opaqueNode *Node
13 | transparentNode *Node
14 | txStack *matstack.TransformStack
15 | bucketCount int
16 | bucket bucketEntries
17 | }
18 |
19 | //factory
20 | func CreateSceneGraph() *SceneGraph {
21 | sceneGraph := &SceneGraph{
22 | opaqueNode: NewNode(),
23 | transparentNode: NewNode(),
24 | txStack: matstack.NewTransformStack(),
25 | }
26 | return sceneGraph
27 | }
28 |
29 | func (sceneGraph *SceneGraph) AddTransparent(spatial Spatial) {
30 | sceneGraph.transparentNode.Add(spatial)
31 | }
32 |
33 | func (sceneGraph *SceneGraph) RemoveTransparent(spatial Spatial, destroy bool) {
34 | sceneGraph.transparentNode.Remove(spatial, destroy)
35 | }
36 |
37 | func (sceneGraph *SceneGraph) Add(spatial Spatial) {
38 | sceneGraph.opaqueNode.Add(spatial)
39 | }
40 |
41 | func (sceneGraph *SceneGraph) Remove(spatial Spatial, destroy bool) {
42 | sceneGraph.opaqueNode.Remove(spatial, destroy)
43 | }
44 |
45 | func (sceneGraph *SceneGraph) RenderScene(renderer Renderer, cameraLocation mgl32.Vec3) {
46 | //setup buckets
47 | sceneGraph.bucketCount = 0
48 | sceneGraph.buildBuckets(sceneGraph.transparentNode, cameraLocation)
49 | sort.Sort(sceneGraph.bucket)
50 | //render buckets
51 | sceneGraph.opaqueNode.Draw(renderer, mgl32.Ident4())
52 | for i := 0; i < sceneGraph.bucketCount; i++ {
53 | entry := sceneGraph.bucket[i]
54 | entry.parent.DrawChild(renderer, entry.transform, entry.spatial)
55 | }
56 | }
57 |
58 | type bucketEntry struct {
59 | spatial Spatial
60 | parent *Node
61 | transform mgl32.Mat4
62 | cameraDelta float32
63 | }
64 |
65 | type bucketEntries []bucketEntry
66 |
67 | func (slice bucketEntries) Len() int {
68 | return len(slice)
69 | }
70 |
71 | func (slice bucketEntries) Less(i, j int) bool {
72 | return slice[i].cameraDelta > slice[j].cameraDelta
73 | }
74 |
75 | func (slice bucketEntries) Swap(i, j int) {
76 | slice[i], slice[j] = slice[j], slice[i]
77 | }
78 |
79 | func (sceneGraph *SceneGraph) buildBuckets(node *Node, cameraLocation mgl32.Vec3) {
80 | sceneGraph.txStack.Push(node.Transform)
81 | for _, child := range node.children {
82 | nextNode, ok := child.(*Node)
83 | if ok {
84 | sceneGraph.buildBuckets(nextNode, cameraLocation)
85 | } else {
86 | transform := sceneGraph.txStack.Peek()
87 | entry := bucketEntry{
88 | spatial: child,
89 | parent: node,
90 | transform: transform,
91 | cameraDelta: util.Vec3LenSq(mgl32.TransformCoordinate(child.Center(), transform).Sub(cameraLocation)),
92 | }
93 | if sceneGraph.bucketCount < len(sceneGraph.bucket) {
94 | sceneGraph.bucket[sceneGraph.bucketCount] = entry
95 | } else {
96 | sceneGraph.bucket = append(sceneGraph.bucket, entry)
97 | }
98 | sceneGraph.bucketCount = sceneGraph.bucketCount + 1
99 | }
100 | }
101 | sceneGraph.txStack.Pop()
102 | }
103 |
--------------------------------------------------------------------------------
/renderer/shader.go:
--------------------------------------------------------------------------------
1 | package renderer
2 |
3 | type Shader struct {
4 | Program uint32
5 | Loaded bool
6 |
7 | Uniforms map[string]interface{}
8 | FragDataLocations []string
9 | InputBuffers int
10 |
11 | textureUnitCounter int32
12 | TextureUnits map[string]int32
13 |
14 | FragSrc, VertSrc, GeoSrc string
15 | }
16 |
17 | func NewShader() *Shader {
18 | return &Shader{
19 | FragDataLocations: []string{"outputColor"},
20 | Uniforms: make(map[string]interface{}),
21 | TextureUnits: make(map[string]int32),
22 | InputBuffers: 1,
23 | }
24 | }
25 |
26 | // AddTexture - allocates a texture unit to the shader
27 | func (shader *Shader) AddTexture(textureName string) int32 {
28 | index, ok := shader.TextureUnits[textureName]
29 | if !ok {
30 | index = shader.textureUnitCounter
31 | shader.textureUnitCounter = shader.textureUnitCounter + 1
32 | shader.TextureUnits[textureName] = index
33 | }
34 |
35 | shader.Uniforms[textureName] = index
36 | return index
37 | }
38 |
39 | func (shader *Shader) Copy() (copy *Shader) {
40 | copy = NewShader()
41 | copy.FragDataLocations = shader.FragDataLocations
42 | copy.FragSrc = shader.FragSrc
43 | copy.VertSrc = shader.VertSrc
44 | copy.GeoSrc = shader.GeoSrc
45 | copy.InputBuffers = shader.InputBuffers
46 | return
47 | }
48 |
--------------------------------------------------------------------------------
/resources/cubemap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/resources/cubemap.png
--------------------------------------------------------------------------------
/resources/cubemapNightSky.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/resources/cubemapNightSky.jpg
--------------------------------------------------------------------------------
/resources/fire.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/resources/fire.png
--------------------------------------------------------------------------------
/resources/majic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/resources/majic.png
--------------------------------------------------------------------------------
/resources/smoke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/resources/smoke.png
--------------------------------------------------------------------------------
/resources/space.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/resources/space.jpg
--------------------------------------------------------------------------------
/resources/spark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/resources/spark.png
--------------------------------------------------------------------------------
/resources/stickman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walesey/go-engine/e229e0e7d42b1d984bf7803a62878acfe39bde9d/resources/stickman.png
--------------------------------------------------------------------------------
/serializer/serialize.go:
--------------------------------------------------------------------------------
1 | package serializer
2 |
3 | import (
4 | "fmt"
5 | "io"
6 |
7 | "github.com/go-gl/mathgl/mgl32"
8 | "github.com/go-gl/mathgl/mgl64"
9 | )
10 |
11 | var GlobalSerializer = Serializer{
12 | OnError: func(err error) {
13 | fmt.Println("serializer error: ", err)
14 | },
15 | }
16 |
17 | func SerializeArgs(args ...interface{}) []byte {
18 | return GlobalSerializer.SerializeArgs(args...)
19 | }
20 |
21 | func Stringfrombytes(r io.Reader) string {
22 | return GlobalSerializer.Stringfrombytes(r)
23 | }
24 |
25 | func Stringbytes(w io.Writer, str string) {
26 | GlobalSerializer.Stringbytes(w, str)
27 | }
28 |
29 | func Float64frombytes(r io.Reader) float64 {
30 | return GlobalSerializer.Float64frombytes(r)
31 | }
32 |
33 | func Float64bytes(w io.Writer, float float64) {
34 | GlobalSerializer.Float64bytes(w, float)
35 | }
36 |
37 | func Float32frombytes(r io.Reader) float32 {
38 | return GlobalSerializer.Float32frombytes(r)
39 | }
40 |
41 | func Float32bytes(w io.Writer, float float32) {
42 | GlobalSerializer.Float32bytes(w, float)
43 | }
44 |
45 | func UInt8frombytes(r io.Reader) uint8 {
46 | return GlobalSerializer.UInt8frombytes(r)
47 | }
48 |
49 | func UInt8Bytes(w io.Writer, i uint8) {
50 | GlobalSerializer.UInt8Bytes(w, i)
51 | }
52 |
53 | func UInt16frombytes(r io.Reader) uint16 {
54 | return GlobalSerializer.UInt16frombytes(r)
55 | }
56 |
57 | func UInt16Bytes(w io.Writer, i uint16) {
58 | GlobalSerializer.UInt16Bytes(w, i)
59 | }
60 |
61 | func UInt32frombytes(r io.Reader) uint32 {
62 | return GlobalSerializer.UInt32frombytes(r)
63 | }
64 |
65 | func UInt32Bytes(w io.Writer, i uint32) {
66 | GlobalSerializer.UInt32Bytes(w, i)
67 | }
68 |
69 | func UInt64frombytes(r io.Reader) uint64 {
70 | return GlobalSerializer.UInt64frombytes(r)
71 | }
72 |
73 | func UInt64Bytes(w io.Writer, i uint64) {
74 | GlobalSerializer.UInt64Bytes(w, i)
75 | }
76 |
77 | func Vector2frombytes(r io.Reader) mgl32.Vec2 {
78 | return GlobalSerializer.Vector2frombytes(r)
79 | }
80 |
81 | func Vector2bytes(w io.Writer, vector mgl32.Vec2) {
82 | GlobalSerializer.Vector2bytes(w, vector)
83 | }
84 |
85 | func Vector2frombytes64(r io.Reader) mgl64.Vec2 {
86 | return GlobalSerializer.Vector2frombytes64(r)
87 | }
88 |
89 | func Vector2bytes64(w io.Writer, vector mgl64.Vec2) {
90 | GlobalSerializer.Vector2bytes64(w, vector)
91 | }
92 |
93 | func Vector3frombytes(r io.Reader) mgl32.Vec3 {
94 | return GlobalSerializer.Vector3frombytes(r)
95 | }
96 |
97 | func Vector3bytes(w io.Writer, vector mgl32.Vec3) {
98 | GlobalSerializer.Vector3bytes(w, vector)
99 | }
100 |
101 | func Vector3frombytes64(r io.Reader) mgl64.Vec3 {
102 | return GlobalSerializer.Vector3frombytes64(r)
103 | }
104 |
105 | func Vector3bytes64(w io.Writer, vector mgl64.Vec3) {
106 | GlobalSerializer.Vector3bytes64(w, vector)
107 | }
108 |
109 | func BoolFromBytes(r io.Reader) bool {
110 | return GlobalSerializer.BoolFromBytes(r)
111 | }
112 |
113 | func BoolBytes(w io.Writer, b bool) {
114 | GlobalSerializer.BoolBytes(w, b)
115 | }
116 |
--------------------------------------------------------------------------------
/serializer/serializer.go:
--------------------------------------------------------------------------------
1 | package serializer
2 |
3 | import (
4 | "io"
5 |
6 | "github.com/walesey/go-engine/util"
7 |
8 | "github.com/go-gl/mathgl/mgl32"
9 | "github.com/go-gl/mathgl/mgl64"
10 | )
11 |
12 | type Serializer struct {
13 | OnError func(error)
14 | }
15 |
16 | func (s Serializer) SerializeArgs(args ...interface{}) []byte {
17 | result, err := util.SerializeArgs(args...)
18 | if err != nil {
19 | s.OnError(err)
20 | }
21 | return result
22 | }
23 |
24 | func (s Serializer) Stringfrombytes(r io.Reader) string {
25 | result, err := util.Stringfrombytes(r)
26 | if err != nil {
27 | s.OnError(err)
28 | }
29 | return result
30 | }
31 |
32 | func (s Serializer) Stringbytes(w io.Writer, str string) {
33 | if err := util.Stringbytes(w, str); err != nil {
34 | s.OnError(err)
35 | }
36 | }
37 |
38 | func (s Serializer) Float64frombytes(r io.Reader) float64 {
39 | result, err := util.Float64frombytes(r)
40 | if err != nil {
41 | s.OnError(err)
42 | }
43 | return result
44 | }
45 |
46 | func (s Serializer) Float64bytes(w io.Writer, float float64) {
47 | if err := util.Float64bytes(w, float); err != nil {
48 | s.OnError(err)
49 | }
50 | }
51 |
52 | func (s Serializer) Float32frombytes(r io.Reader) float32 {
53 | result, err := util.Float32frombytes(r)
54 | if err != nil {
55 | s.OnError(err)
56 | }
57 | return result
58 | }
59 |
60 | func (s Serializer) Float32bytes(w io.Writer, float float32) {
61 | if err := util.Float32bytes(w, float); err != nil {
62 | s.OnError(err)
63 | }
64 | }
65 |
66 | func (s Serializer) UInt8frombytes(r io.Reader) uint8 {
67 | result, err := util.UInt8frombytes(r)
68 | if err != nil {
69 | s.OnError(err)
70 | }
71 | return result
72 | }
73 |
74 | func (s Serializer) UInt8Bytes(w io.Writer, i uint8) {
75 | if err := util.UInt8Bytes(w, i); err != nil {
76 | s.OnError(err)
77 | }
78 | }
79 |
80 | func (s Serializer) UInt16frombytes(r io.Reader) uint16 {
81 | result, err := util.UInt16frombytes(r)
82 | if err != nil {
83 | s.OnError(err)
84 | }
85 | return result
86 | }
87 |
88 | func (s Serializer) UInt16Bytes(w io.Writer, i uint16) {
89 | if err := util.UInt16Bytes(w, i); err != nil {
90 | s.OnError(err)
91 | }
92 | }
93 |
94 | func (s Serializer) UInt32frombytes(r io.Reader) uint32 {
95 | result, err := util.UInt32frombytes(r)
96 | if err != nil {
97 | s.OnError(err)
98 | }
99 | return result
100 | }
101 |
102 | func (s Serializer) UInt32Bytes(w io.Writer, i uint32) {
103 | if err := util.UInt32Bytes(w, i); err != nil {
104 | s.OnError(err)
105 | }
106 | }
107 |
108 | func (s Serializer) UInt64frombytes(r io.Reader) uint64 {
109 | result, err := util.UInt64frombytes(r)
110 | if err != nil {
111 | s.OnError(err)
112 | }
113 | return result
114 | }
115 |
116 | func (s Serializer) UInt64Bytes(w io.Writer, i uint64) {
117 | if err := util.UInt64Bytes(w, i); err != nil {
118 | s.OnError(err)
119 | }
120 | }
121 |
122 | func (s Serializer) Vector2frombytes(r io.Reader) mgl32.Vec2 {
123 | result, err := util.Vector2frombytes(r)
124 | if err != nil {
125 | s.OnError(err)
126 | }
127 | return result
128 | }
129 |
130 | func (s Serializer) Vector2bytes(w io.Writer, vector mgl32.Vec2) {
131 | if err := util.Vector2bytes(w, vector); err != nil {
132 | s.OnError(err)
133 | }
134 | }
135 |
136 | func (s Serializer) Vector2frombytes64(r io.Reader) mgl64.Vec2 {
137 | result, err := util.Vector2frombytes64(r)
138 | if err != nil {
139 | s.OnError(err)
140 | }
141 | return result
142 | }
143 |
144 | func (s Serializer) Vector2bytes64(w io.Writer, vector mgl64.Vec2) {
145 | if err := util.Vector2bytes64(w, vector); err != nil {
146 | s.OnError(err)
147 | }
148 | }
149 |
150 | func (s Serializer) Vector3frombytes(r io.Reader) mgl32.Vec3 {
151 | result, err := util.Vector3frombytes(r)
152 | if err != nil {
153 | s.OnError(err)
154 | }
155 | return result
156 | }
157 |
158 | func (s Serializer) Vector3bytes(w io.Writer, vector mgl32.Vec3) {
159 | if err := util.Vector3bytes(w, vector); err != nil {
160 | s.OnError(err)
161 | }
162 | }
163 |
164 | func (s Serializer) Vector3frombytes64(r io.Reader) mgl64.Vec3 {
165 | result, err := util.Vector3frombytes64(r)
166 | if err != nil {
167 | s.OnError(err)
168 | }
169 | return result
170 | }
171 |
172 | func (s Serializer) Vector3bytes64(w io.Writer, vector mgl64.Vec3) {
173 | if err := util.Vector3bytes64(w, vector); err != nil {
174 | s.OnError(err)
175 | }
176 | }
177 |
178 | func (s Serializer) BoolFromBytes(r io.Reader) bool {
179 | result, err := util.BoolFromBytes(r)
180 | if err != nil {
181 | s.OnError(err)
182 | }
183 | return result
184 | }
185 |
186 | func (s Serializer) BoolBytes(w io.Writer, b bool) {
187 | if err := util.BoolBytes(w, b); err != nil {
188 | s.OnError(err)
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/shaderBuilder/README.md:
--------------------------------------------------------------------------------
1 | # ShaderBuilder
2 |
3 | A Shader preprocessor that handles the following # macros
4 | * #include - works like c include for local shader files only
5 | * #vert / #endvert - code in this section is only outputted to the vertex shader.
6 | * #frag / #endfrag
7 | * #geo / #geo
8 |
9 | The output of the preprocessor is a single shader file to be compiled by opengl etc.
10 |
11 | ### Example Usage
12 |
13 | ```
14 | shaderBuilder path/to/file.glsl vert > out.vert
15 | shaderBuilder path/to/file.glsl frag > out.frag
16 | ```
17 |
--------------------------------------------------------------------------------
/shaderBuilder/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "os"
7 | "regexp"
8 |
9 | "github.com/walesey/go-engine/shaderBuilder/parser"
10 | )
11 |
12 | func main() {
13 | srcFile := "./index.glsl"
14 | if len(os.Args) >= 2 {
15 | srcFile = os.Args[1]
16 | }
17 |
18 | mode := "vert"
19 | if len(os.Args) >= 3 {
20 | mode = os.Args[2]
21 | }
22 |
23 | out := new(bytes.Buffer)
24 | switch mode {
25 | case "vert":
26 | parser.ParseFile(srcFile, out, nil, nil)
27 | case "frag":
28 | parser.ParseFile(srcFile, nil, out, nil)
29 | case "geo":
30 | parser.ParseFile(srcFile, nil, nil, out)
31 | default:
32 | panic("Invalid shader type: " + mode)
33 | }
34 | output := out.String()
35 |
36 | re := regexp.MustCompile("\n[\\s]+\n[\\s]+\n")
37 | fmt.Println(re.ReplaceAllString(output, "\n\n"))
38 | }
39 |
--------------------------------------------------------------------------------
/shaderBuilder/parser/expression.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | "math"
5 | "strconv"
6 | )
7 |
8 | type Variable struct {
9 | Name string
10 | Value float64
11 | }
12 |
13 | type Expression interface {
14 | Evaluate(variables ...Variable) float64
15 | }
16 |
17 | type BinaryExpression struct {
18 | Operator Token
19 | Left Expression
20 | Right Expression
21 | }
22 |
23 | func (b BinaryExpression) Evaluate(variables ...Variable) float64 {
24 | switch b.Operator {
25 | case PLUS:
26 | return b.Left.Evaluate(variables...) + b.Right.Evaluate(variables...)
27 | case MINUS:
28 | return b.Left.Evaluate(variables...) - b.Right.Evaluate(variables...)
29 | case MULTIPLY:
30 | return b.Left.Evaluate(variables...) * b.Right.Evaluate(variables...)
31 | case SLASH:
32 | return b.Left.Evaluate(variables...) / b.Right.Evaluate(variables...)
33 | case REMAINDER:
34 | leftInt, rightInt := int(b.Left.Evaluate(variables...)), int(b.Right.Evaluate(variables...))
35 | return float64(leftInt % rightInt)
36 | case POWER:
37 | return math.Pow(b.Left.Evaluate(variables...), b.Right.Evaluate(variables...))
38 | default:
39 | }
40 | return 0
41 | }
42 |
43 | type Identifier struct {
44 | Name string
45 | }
46 |
47 | func (i Identifier) Evaluate(variables ...Variable) float64 {
48 | for _, variable := range variables {
49 | if variable.Name == i.Name {
50 | return variable.Value
51 | }
52 | }
53 | return 0
54 | }
55 |
56 | type Literal struct {
57 | Value float64
58 | }
59 |
60 | func (l Literal) Evaluate(variables ...Variable) float64 {
61 | return l.Value
62 | }
63 |
64 | func (p *Parser) parseSignedExpression() Expression {
65 | var left Expression = Literal{Value: 0}
66 |
67 | if p.token == PLUS || p.token == MINUS {
68 | if p.token == MINUS {
69 | left = Literal{-1}
70 | } else {
71 | left = Literal{1}
72 | }
73 | p.nextNoWhitespace()
74 |
75 | left = BinaryExpression{
76 | Operator: MULTIPLY,
77 | Left: left,
78 | Right: p.parseExpression(),
79 | }
80 | }
81 |
82 | return left
83 | }
84 |
85 | func (p *Parser) parseGroupExpression() Expression {
86 | exp := p.parseSignedExpression()
87 |
88 | if p.token == LEFT_PARENTHESIS {
89 | p.nextNoWhitespace()
90 | exp = p.parseExpression()
91 |
92 | if p.token != RIGHT_PARENTHESIS {
93 | p.unexpectedError()
94 | }
95 | p.nextNoWhitespace()
96 | }
97 |
98 | return exp
99 | }
100 |
101 | func (p *Parser) parseIdentifierExpression() Expression {
102 | exp := p.parseGroupExpression()
103 |
104 | if p.token == IDENTIFIER {
105 | exp = Identifier{Name: p.literal}
106 | p.nextNoWhitespace()
107 | }
108 |
109 | return exp
110 | }
111 |
112 | func (p *Parser) parseLiteralExpression() Expression {
113 | exp := p.parseIdentifierExpression()
114 |
115 | if p.token == NUMBER {
116 | value, _ := strconv.ParseFloat(p.literal, 64)
117 | exp = Literal{Value: value}
118 | p.nextNoWhitespace()
119 | }
120 |
121 | return exp
122 | }
123 |
124 | func (p *Parser) parsePowerExpression() Expression {
125 | nextParse := p.parseLiteralExpression
126 | left := nextParse()
127 |
128 | for p.token == POWER {
129 | tkn := p.token
130 | p.nextNoWhitespace()
131 |
132 | left = BinaryExpression{
133 | Operator: tkn,
134 | Left: left,
135 | Right: nextParse(),
136 | }
137 | }
138 |
139 | return left
140 | }
141 |
142 | func (p *Parser) parseMultiplicativeExpression() Expression {
143 | nextParse := p.parsePowerExpression
144 | left := nextParse()
145 |
146 | for p.token == MULTIPLY || p.token == SLASH || p.token == REMAINDER {
147 | tkn := p.token
148 | p.nextNoWhitespace()
149 |
150 | left = BinaryExpression{
151 | Operator: tkn,
152 | Left: left,
153 | Right: nextParse(),
154 | }
155 | }
156 |
157 | return left
158 | }
159 |
160 | func (p *Parser) parseAdditiveExpression() Expression {
161 | nextParse := p.parseMultiplicativeExpression
162 | left := nextParse()
163 |
164 | for p.token == PLUS || p.token == MINUS {
165 | tkn := p.token
166 | p.nextNoWhitespace()
167 |
168 | left = BinaryExpression{
169 | Operator: tkn,
170 | Left: left,
171 | Right: nextParse(),
172 | }
173 | }
174 |
175 | return left
176 | }
177 |
178 | func (p *Parser) parseExpression() Expression {
179 | return p.parseAdditiveExpression()
180 | }
181 |
--------------------------------------------------------------------------------
/shaderBuilder/parser/lexer.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import "bytes"
4 |
5 | const eof = rune(0)
6 |
7 | func (p *Parser) scan() (tkn Token, literal string) {
8 | p.read()
9 |
10 | if isWhitespace(p.chr) {
11 | tkn, literal = p.scanWhitespace()
12 | return
13 | } else if isLetter(p.chr) {
14 | tkn, literal = p.scanIdentifier()
15 | return
16 | } else if isNumeric(p.chr) {
17 | tkn, literal = NUMBER, p.scanNumber()
18 | return
19 | }
20 |
21 | switch p.chr {
22 | case eof:
23 | tkn, literal = EOF, ""
24 | case '(':
25 | tkn, literal = LEFT_PARENTHESIS, string(p.chr)
26 | case ')':
27 | tkn, literal = RIGHT_PARENTHESIS, string(p.chr)
28 | case '#':
29 | tkn, literal = HASH, string(p.chr)
30 | case '+':
31 | tkn, literal = PLUS, string(p.chr)
32 | case '-':
33 | tkn, literal = MINUS, string(p.chr)
34 | case '*':
35 | tkn, literal = MULTIPLY, string(p.chr)
36 | case '/':
37 | tkn, literal = SLASH, string(p.chr)
38 | case '%':
39 | tkn, literal = REMAINDER, string(p.chr)
40 | case '^':
41 | tkn, literal = POWER, string(p.chr)
42 | case '"':
43 | tkn, literal = STRING, p.scanString()
44 | default:
45 | tkn, literal = UNKNOWN, string(p.chr)
46 | }
47 |
48 | return
49 | }
50 |
51 | func (p *Parser) read() {
52 | chr, _, err := p.in.ReadRune()
53 | if err != nil {
54 | p.chr = eof
55 | } else {
56 | p.chr = chr
57 | }
58 | }
59 |
60 | func (p *Parser) unread() {
61 | _ = p.in.UnreadRune()
62 | }
63 |
64 | func isWhitespace(ch rune) bool {
65 | return ch == ' ' || ch == '\t' || ch == '\n'
66 | }
67 |
68 | func isLetter(ch rune) bool {
69 | return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
70 | }
71 |
72 | func isDigit(ch rune) bool {
73 | return '0' <= ch && ch <= '9'
74 | }
75 |
76 | func isNumeric(ch rune) bool {
77 | return isDigit(ch) || ch == '.'
78 | }
79 |
80 | func isIdentifierRune(ch rune) bool {
81 | return isLetter(ch) || isDigit(ch) || ch == '_'
82 | }
83 |
84 | func (p *Parser) scanWhitespace() (tkn Token, literal string) {
85 | var buf bytes.Buffer
86 | buf.WriteRune(p.chr)
87 |
88 | for {
89 | if p.read(); p.chr == eof {
90 | break
91 | } else if !isWhitespace(p.chr) {
92 | p.unread()
93 | break
94 | } else {
95 | buf.WriteRune(p.chr)
96 | }
97 | }
98 |
99 | tkn, literal = WHITESPACE, buf.String()
100 | return
101 | }
102 |
103 | func (p *Parser) scanIdentifier() (tkn Token, literal string) {
104 | var buf bytes.Buffer
105 | buf.WriteRune(p.chr)
106 |
107 | for {
108 | if p.read(); p.chr == eof {
109 | break
110 | } else if !isIdentifierRune(p.chr) {
111 | p.unread()
112 | break
113 | } else {
114 | buf.WriteRune(p.chr)
115 | }
116 | }
117 |
118 | tkn, literal = IDENTIFIER, buf.String()
119 |
120 | // is it a keyword
121 | tkn = checkKeyword(literal)
122 |
123 | return
124 | }
125 |
126 | func (p *Parser) scanNumber() (literal string) {
127 | var buf bytes.Buffer
128 | buf.WriteRune(p.chr)
129 |
130 | for {
131 | if p.read(); p.chr == eof {
132 | break
133 | } else if !isNumeric(p.chr) {
134 | p.unread()
135 | break
136 | } else {
137 | buf.WriteRune(p.chr)
138 | }
139 | }
140 |
141 | return buf.String()
142 | }
143 |
144 | func (p *Parser) scanString() string {
145 | var buf bytes.Buffer
146 |
147 | for {
148 | if p.read(); p.chr == eof || p.chr == '"' {
149 | break
150 | } else {
151 | buf.WriteRune(p.chr)
152 | }
153 | }
154 |
155 | return buf.String()
156 | }
157 |
--------------------------------------------------------------------------------
/shaderBuilder/parser/parser_test.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | "bytes"
5 | "os"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/assert"
9 | "regexp"
10 | )
11 |
12 | func fixNewLines(str string) string {
13 | re := regexp.MustCompile(`[\n|\r]+`)
14 | return re.ReplaceAllString(str, "\n")
15 | }
16 |
17 | func TestGenerator(t *testing.T) {
18 | expectedFragFd, err := os.Open("./test/expected.frag")
19 | assert.NoError(t, err)
20 | defer expectedFragFd.Close()
21 |
22 | expectedVertFd, err := os.Open("./test/expected.vert")
23 | assert.NoError(t, err)
24 | defer expectedVertFd.Close()
25 |
26 | shaderPath := "./test/test.glsl"
27 | frag := new(bytes.Buffer)
28 | vert := new(bytes.Buffer)
29 | err = ParseFile(shaderPath, vert, frag, nil)
30 |
31 | expectedFrag := new(bytes.Buffer)
32 | expectedFrag.ReadFrom(expectedFragFd)
33 | assert.Equal(t, fixNewLines(expectedFrag.String()), fixNewLines(frag.String()))
34 |
35 | expectedVert := new(bytes.Buffer)
36 | expectedVert.ReadFrom(expectedVertFd)
37 | assert.Equal(t, fixNewLines(expectedVert.String()), fixNewLines(vert.String()))
38 | }
39 |
--------------------------------------------------------------------------------
/shaderBuilder/parser/test/expected.frag:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | float import1() {
4 | return 1.0;
5 | }
6 |
7 | uniform float lookupTable[10] = float[] (0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1);
8 | uniform float lookupTable2[10] = float[] (-0.01, -0.02, -0.03, -0.04, -0.05, -0.06, -0.07, -0.08, -0.09, -0.1);
9 |
10 | vec4 fragFunc() {
11 | output = vec4(0.5);
12 | for (int i = 0; i < 10; ++i) {
13 | output += vec4(lookupTable[i]);
14 | }
15 | return vec4(0.5);
16 | }
17 |
18 | void main() {
19 | gl_FragColor = fragFunc();
20 | }
21 |
--------------------------------------------------------------------------------
/shaderBuilder/parser/test/expected.vert:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | float import1() {
4 | return 1.0;
5 | }
6 |
7 | vec4 vertFunc() {
8 | return vec4(1.0);
9 | }
10 |
11 | void main() {
12 | gl_Position = vertFunc();
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/shaderBuilder/parser/test/includeTest.glsl:
--------------------------------------------------------------------------------
1 | #include "includeTest2.glsl"
2 |
3 | #frag
4 | uniform #lookup 10 lookupTable ((i + 1) / 100.0);
5 | uniform #lookup 10 lookupTable2 ((i + 1) / -100.0);
6 |
7 | vec4 fragFunc() {
8 | output = vec4(0.5);
9 | for (int i = 0; i < 10; ++i) {
10 | output += vec4(lookupTable[i]);
11 | }
12 | return vec4(0.5);
13 | }
14 | #endfrag
15 |
16 | #vert
17 | vec4 vertFunc() {
18 | return vec4(1.0);
19 | }
20 | #endvert
--------------------------------------------------------------------------------
/shaderBuilder/parser/test/includeTest2.glsl:
--------------------------------------------------------------------------------
1 | float import1() {
2 | return 1.0;
3 | }
--------------------------------------------------------------------------------
/shaderBuilder/parser/test/test.glsl:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | #include "includeTest.glsl"
4 | #include "includeTest2.glsl"
5 |
6 | void main() {
7 | #vert
8 | gl_Position = vertFunc();
9 | #endvert
10 |
11 | #frag
12 | gl_FragColor = fragFunc();
13 | #endfrag
14 | }
15 |
--------------------------------------------------------------------------------
/shaderBuilder/parser/token.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | type Token int
4 |
5 | const (
6 | _ Token = iota
7 |
8 | EOF
9 | WHITESPACE
10 | IDENTIFIER
11 | STRING
12 | NUMBER
13 |
14 | LEFT_PARENTHESIS // (
15 | RIGHT_PARENTHESIS // )
16 |
17 | HASH // #
18 | PLUS // +
19 | MINUS // -
20 | MULTIPLY // *
21 | SLASH // /
22 | REMAINDER // %
23 | POWER // ^
24 | UNKNOWN
25 |
26 | // keywords
27 | INCLUDE
28 | VERT
29 | FRAG
30 | GEO
31 | ENDVERT
32 | ENDFRAG
33 | ENDGEO
34 | LOOKUP
35 | )
36 |
37 | func checkKeyword(literal string) Token {
38 | switch literal {
39 | case "include":
40 | return INCLUDE
41 | case "vert":
42 | return VERT
43 | case "frag":
44 | return FRAG
45 | case "geo":
46 | return GEO
47 | case "endvert":
48 | return ENDVERT
49 | case "endfrag":
50 | return ENDFRAG
51 | case "endgeo":
52 | return ENDGEO
53 | case "lookup":
54 | return LOOKUP
55 | }
56 | return IDENTIFIER
57 | }
58 |
--------------------------------------------------------------------------------
/shaders/basic.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | #include "./lib/base.glsl"
4 | #include "./lib/worldTransform.glsl"
5 | #include "./lib/textures.glsl"
6 | #include "./lib/ambientLight.glsl"
7 | #include "./lib/pointLights.glsl"
8 | #include "./lib/directionalLights.glsl"
9 |
10 | void main() {
11 | textures();
12 |
13 | #vert
14 | worldTransform();
15 | gl_Position = projection * camera * model * vec4(vert, 1);
16 | #endvert
17 |
18 | #frag
19 | if (unlit) {
20 | outputColor = diffuse;
21 | } else {
22 | vec3 finalColor = ambientLight(diffuse) + pointLights(diffuse, specular, normalValue) + directionalLights(diffuse, specular, normalValue);
23 | outputColor = vec4(finalColor, diffuse.a);
24 | }
25 | #endfrag
26 | }
--------------------------------------------------------------------------------
/shaders/build/basic.frag:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | uniform mat4 projection;
4 | uniform mat4 camera;
5 | uniform mat4 model;
6 | uniform mat4 modelNormal;
7 | uniform vec3 cameraTranslation;
8 |
9 | uniform bool unlit;
10 | uniform bool useTextures;
11 |
12 | layout(location = 0) out vec4 outputColor;
13 |
14 | in vec3 worldVertex;
15 | in vec3 worldNormal;
16 | in vec3 eyeDirection;
17 | in mat3 TBNMatrix;
18 | in mat3 inverseTBNMatrix;
19 |
20 | uniform sampler2D normalMap;
21 | uniform sampler2D diffuseMap;
22 | uniform sampler2D specularMap;
23 | uniform sampler2D aoMap;
24 |
25 | in vec2 fragTexCoord;
26 | in vec4 fragColor;
27 |
28 | vec4 normalValue;
29 | vec4 diffuse;
30 | vec4 specular;
31 | vec4 ao;
32 |
33 | vec2 repeatTextCoord() {
34 | float textureX = fragTexCoord.x - int(fragTexCoord.x);
35 | float textureY = fragTexCoord.y - int(fragTexCoord.y);
36 | if (fragTexCoord.x < 0) {textureX = textureX + 1.0;}
37 | if (fragTexCoord.y < 0) {textureY = textureY + 1.0;}
38 | return vec2(textureX, textureY);
39 | }
40 |
41 | void textures() {
42 | vec2 overflowTextCoord = repeatTextCoord();
43 |
44 | // multiply color by diffuse map. use only color if no map is provided
45 | if (useTextures) {
46 | diffuse = fragColor * texture(diffuseMap, overflowTextCoord);
47 | specular = texture(specularMap, overflowTextCoord);
48 | normalValue = texture(normalMap, overflowTextCoord);
49 | ao = texture(aoMap, overflowTextCoord);
50 | } else {
51 | diffuse = fragColor;
52 | specular = vec4(0);
53 | normalValue = vec4(0);
54 | ao = vec4(1);
55 | }
56 | }
57 |
58 | uniform vec3 ambientLightValue;
59 |
60 | vec3 ambientLight(vec4 diffuse) {
61 | return ambientLightValue * diffuse.rgb;
62 | }
63 |
64 | float pow2(float x) {
65 | return x*x;
66 | }
67 |
68 | float pow3(float x) {
69 | return x*x*x;
70 | }
71 |
72 |
73 | vec3 directLight( vec3 light, vec3 direction, vec4 diffuse, vec4 specular, vec4 normalValue ) {
74 | vec3 normal_tangentSpace = (normalValue.xyz*2) - 1;
75 | vec3 direction_tangentSpace = direction * TBNMatrix;
76 | vec3 eyeDirection_tangentSpace = eyeDirection * TBNMatrix;
77 | vec3 reflectedEye_tangentSpace = reflect( eyeDirection_tangentSpace, normal_tangentSpace );
78 |
79 | float diffuseMultiplier = max(0.0, dot(normal_tangentSpace, -direction_tangentSpace));
80 |
81 | float specularMultiplier = pow2(max(0.0, dot(reflectedEye_tangentSpace, -direction_tangentSpace)));
82 |
83 | vec3 color = (diffuseMultiplier * diffuse.rgb) + (specularMultiplier * specular.rgb);
84 |
85 | return color * light;
86 | }
87 |
88 |
89 |
90 | #define MAX_POINT_LIGHTS 4
91 |
92 | uniform int nbPointLights;
93 | uniform vec4 pointLightPositions[ MAX_POINT_LIGHTS ];
94 | uniform vec4 pointLightValues[ MAX_POINT_LIGHTS ];
95 |
96 | vec3 pointLights(vec4 diffuse, vec4 specular, vec4 normalValue) {
97 | vec3 totalLight = vec3(0.0, 0.0, 0.0);
98 | for (int i=0; i < nbPointLights; i++) {
99 | vec3 LightPos = pointLightPositions[i].rgb;
100 | vec3 LightValue = pointLightValues[i].rgb;
101 |
102 | vec3 v = worldVertex - LightPos;
103 | float lightDistance = dot(v, v);
104 | float brightness = 1.0 / lightDistance;
105 |
106 | vec3 worldLightDir = normalize(v);
107 | vec3 light = brightness*LightValue;
108 |
109 | totalLight += directLight(light, worldLightDir, diffuse, specular, normalValue);
110 | }
111 | return totalLight;
112 | }
113 |
114 | #define MAX_DIRECTIONAL_LIGHTS 4
115 |
116 | uniform int nbDirectionalLights;
117 | uniform vec4 directionalLightVectors[ MAX_DIRECTIONAL_LIGHTS ];
118 | uniform vec4 directionalLightValues[ MAX_DIRECTIONAL_LIGHTS ];
119 |
120 | vec3 directionalLights(vec4 diffuse, vec4 specular, vec4 normalValue) {
121 | vec3 totalLight = vec3(0.0, 0.0, 0.0);
122 | for (int i=0; i < nbDirectionalLights; i++) {
123 | vec3 LightDirection = directionalLightVectors[i].rgb;
124 | vec3 LightValue = directionalLightValues[i].rgb;
125 |
126 | totalLight += directLight(LightValue, LightDirection, diffuse, specular, normalValue);
127 | }
128 | return totalLight;
129 | }
130 |
131 |
132 | void main() {
133 | textures();
134 |
135 | if (unlit) {
136 | outputColor = diffuse;
137 | } else {
138 | vec3 finalColor = ambientLight(diffuse) + pointLights(diffuse, specular, normalValue) + directionalLights(diffuse, specular, normalValue);
139 | outputColor = vec4(finalColor, diffuse.a);
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/shaders/build/basic.vert:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | uniform mat4 projection;
4 | uniform mat4 camera;
5 | uniform mat4 model;
6 | uniform mat4 modelNormal;
7 | uniform vec3 cameraTranslation;
8 |
9 | uniform bool unlit;
10 | uniform bool useTextures;
11 |
12 |
13 | in vec3 vert;
14 | in vec3 normal;
15 | in vec2 texCoord;
16 | in vec4 color;
17 |
18 | out vec3 worldVertex;
19 | out vec3 worldNormal;
20 | out vec3 eyeDirection;
21 | out mat3 TBNMatrix;
22 | out mat3 inverseTBNMatrix;
23 |
24 | void worldTransform() {
25 | worldVertex = (model * vec4(vert,1)).xyz;
26 | worldNormal = (modelNormal * vec4(normal,1)).xyz;
27 | worldNormal = normalize(worldNormal);
28 | eyeDirection = normalize(worldVertex - cameraTranslation);
29 |
30 | // generate arbitrary tangent and bitangent to the normal
31 | vec3 tangent = cross(normal, normal + vec3(-1));
32 | vec3 bitangent = cross(normal, tangent);
33 | vec3 worldTangent = normalize((modelNormal * vec4(tangent,1)).xyz);
34 | vec3 worldBitangent = normalize((modelNormal * vec4(bitangent,1)).xyz);
35 |
36 | //tangent space conversion - worldToTangent
37 | TBNMatrix = mat3(worldTangent, worldBitangent, worldNormal);
38 | inverseTBNMatrix = inverse(TBNMatrix);
39 | }
40 |
41 | out vec2 fragTexCoord;
42 | out vec4 fragColor;
43 |
44 | void textures() {
45 | fragTexCoord = texCoord;
46 | fragColor = color;
47 | }
48 |
49 | float pow2(float x) {
50 | return x*x;
51 | }
52 |
53 | float pow3(float x) {
54 | return x*x*x;
55 | }
56 |
57 |
58 |
59 | void main() {
60 | textures();
61 |
62 |
63 | worldTransform();
64 | gl_Position = projection * camera * model * vec4(vert, 1);
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/shaders/build/diffuseSpecular.vert:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | uniform mat4 projection;
4 | uniform mat4 camera;
5 | uniform mat4 model;
6 | uniform mat4 modelNormal;
7 | uniform vec3 cameraTranslation;
8 |
9 | uniform bool unlit;
10 | uniform bool useTextures;
11 |
12 |
13 | in vec3 vert;
14 | in vec3 normal;
15 | in vec2 texCoord;
16 | in vec4 color;
17 |
18 | out vec3 worldVertex;
19 | out vec3 worldNormal;
20 | out vec3 eyeDirection;
21 | out mat3 TBNMatrix;
22 | out mat3 inverseTBNMatrix;
23 |
24 | void worldTransform() {
25 | worldVertex = (model * vec4(vert,1)).xyz;
26 | worldNormal = (modelNormal * vec4(normal,1)).xyz;
27 | worldNormal = normalize(worldNormal);
28 | eyeDirection = normalize(worldVertex - cameraTranslation);
29 |
30 | // generate arbitrary tangent and bitangent to the normal
31 | vec3 tangent = cross(normal, normal + vec3(-1));
32 | vec3 bitangent = cross(normal, tangent);
33 | vec3 worldTangent = normalize((modelNormal * vec4(tangent,1)).xyz);
34 | vec3 worldBitangent = normalize((modelNormal * vec4(bitangent,1)).xyz);
35 |
36 | //tangent space conversion - worldToTangent
37 | TBNMatrix = mat3(worldTangent, worldBitangent, worldNormal);
38 | inverseTBNMatrix = inverse(TBNMatrix);
39 | }
40 |
41 | out vec2 fragTexCoord;
42 | out vec4 fragColor;
43 |
44 | void textures() {
45 | fragTexCoord = texCoord;
46 | fragColor = color;
47 | }
48 |
49 | float pow2(float x) {
50 | return x*x;
51 | }
52 |
53 | float pow3(float x) {
54 | return x*x*x;
55 | }
56 |
57 |
58 |
59 | void roughnessTexture() {}
60 |
61 | void glowOutput() {}
62 |
63 | void main() {
64 | textures();
65 | roughnessTexture();
66 | glowOutput();
67 |
68 |
69 | worldTransform();
70 | gl_Position = projection * camera * model * vec4(vert, 1);
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/shaders/build/pbr.vert:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | uniform mat4 projection;
4 | uniform mat4 camera;
5 | uniform mat4 model;
6 | uniform mat4 modelNormal;
7 | uniform vec3 cameraTranslation;
8 |
9 | uniform bool unlit;
10 | uniform bool useTextures;
11 |
12 |
13 | in vec3 vert;
14 | in vec3 normal;
15 | in vec2 texCoord;
16 | in vec4 color;
17 |
18 | out vec3 worldVertex;
19 | out vec3 worldNormal;
20 | out vec3 eyeDirection;
21 | out mat3 TBNMatrix;
22 | out mat3 inverseTBNMatrix;
23 |
24 | void worldTransform() {
25 | worldVertex = (model * vec4(vert,1)).xyz;
26 | worldNormal = (modelNormal * vec4(normal,1)).xyz;
27 | worldNormal = normalize(worldNormal);
28 | eyeDirection = normalize(worldVertex - cameraTranslation);
29 |
30 | // generate arbitrary tangent and bitangent to the normal
31 | vec3 tangent = cross(normal, normal + vec3(-1));
32 | vec3 bitangent = cross(normal, tangent);
33 | vec3 worldTangent = normalize((modelNormal * vec4(tangent,1)).xyz);
34 | vec3 worldBitangent = normalize((modelNormal * vec4(bitangent,1)).xyz);
35 |
36 | //tangent space conversion - worldToTangent
37 | TBNMatrix = mat3(worldTangent, worldBitangent, worldNormal);
38 | inverseTBNMatrix = inverse(TBNMatrix);
39 | }
40 |
41 | out vec2 fragTexCoord;
42 | out vec4 fragColor;
43 |
44 | void textures() {
45 | fragTexCoord = texCoord;
46 | fragColor = color;
47 | }
48 |
49 | void metalnessTexture() {}
50 |
51 | void roughnessTexture() {}
52 |
53 | float pow2(float x) {
54 | return x*x;
55 | }
56 |
57 | float pow3(float x) {
58 | return x*x*x;
59 | }
60 |
61 | void glowOutput() {}
62 |
63 | void main() {
64 | textures();
65 | metalnessTexture();
66 | roughnessTexture();
67 | glowOutput();
68 |
69 |
70 | worldTransform();
71 | gl_Position = projection * camera * model * vec4(vert, 1);
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/shaders/build/pbrComposite.vert:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | uniform mat4 projection;
4 | uniform mat4 camera;
5 | uniform mat4 model;
6 | uniform mat4 modelNormal;
7 | uniform vec3 cameraTranslation;
8 |
9 | uniform bool unlit;
10 | uniform bool useTextures;
11 |
12 |
13 | in vec3 vert;
14 | in vec3 normal;
15 | in vec2 texCoord;
16 | in vec4 color;
17 |
18 | out vec3 worldVertex;
19 | out vec3 worldNormal;
20 | out vec3 eyeDirection;
21 | out mat3 TBNMatrix;
22 | out mat3 inverseTBNMatrix;
23 |
24 | void worldTransform() {
25 | worldVertex = (model * vec4(vert,1)).xyz;
26 | worldNormal = (modelNormal * vec4(normal,1)).xyz;
27 | worldNormal = normalize(worldNormal);
28 | eyeDirection = normalize(worldVertex - cameraTranslation);
29 |
30 | // generate arbitrary tangent and bitangent to the normal
31 | vec3 tangent = cross(normal, normal + vec3(-1));
32 | vec3 bitangent = cross(normal, tangent);
33 | vec3 worldTangent = normalize((modelNormal * vec4(tangent,1)).xyz);
34 | vec3 worldBitangent = normalize((modelNormal * vec4(bitangent,1)).xyz);
35 |
36 | //tangent space conversion - worldToTangent
37 | TBNMatrix = mat3(worldTangent, worldBitangent, worldNormal);
38 | inverseTBNMatrix = inverse(TBNMatrix);
39 | }
40 |
41 | out vec2 fragTexCoord;
42 | out vec4 fragColor;
43 |
44 | void textures() {
45 | fragTexCoord = texCoord;
46 | fragColor = color;
47 | }
48 |
49 | void pbrCompositeTextures() {}
50 |
51 | float pow2(float x) {
52 | return x*x;
53 | }
54 |
55 | float pow3(float x) {
56 | return x*x*x;
57 | }
58 |
59 | void glowOutput() {}
60 |
61 | void main() {
62 | textures();
63 | pbrCompositeTextures();
64 | glowOutput();
65 |
66 |
67 | worldTransform();
68 | gl_Position = projection * camera * model * vec4(vert, 1);
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/shaders/build/postEffects/cell.frag:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | uniform sampler2D tex0;
4 | in vec2 fragTexCoord;
5 | out vec4 outputColor;
6 |
7 | float cell( float source ) {
8 | if (source < 0.3) {
9 | return 0;
10 | } else if (source < 0.6) {
11 | return 0.3;
12 | } else if (source < 0.9) {
13 | return 0.6;
14 | }
15 | return 1;
16 | }
17 |
18 |
19 | void main() {
20 |
21 | vec4 finalColor = vec4(0,0,0,1);
22 | vec4 source = texture(tex0, fragTexCoord);
23 | finalColor = vec4( cell(source.r), cell(source.g), cell(source.b), cell(source.a) );
24 | outputColor = finalColor;
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/shaders/build/postEffects/cell.vert:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 |
4 | in vec2 vert;
5 | in vec2 texCoord;
6 | out vec2 fragTexCoord;
7 |
8 | void main() {
9 |
10 | gl_Position = vec4(vert, 0.0, 1.0);
11 | fragTexCoord = texCoord;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/shaders/build/postEffects/glow.frag:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | uniform sampler2D tex0;
4 | uniform sampler2D tex1;
5 | in vec2 fragTexCoord;
6 | out vec4 outputColor;
7 |
8 | uniform float sample = 2.0;
9 | uniform float iterations = 8;
10 | uniform float gaussian[100] = float[] (0.23696313590506837, 0.22576837913554718, 0.2149304965314546, 0.20444927242920974, 0.19432366696918377, 0.1845518595211794, 0.17513129301958733, 0.1660587189558863, 0.15733024278516244, 0.14894136951338266, 0.14088704924316767, 0.13316172246762595, 0.12575936491431866, 0.1186735317544968, 0.11189740100626834, 0.10542381597419066, 0.09924532658183352, 0.09335422946800571, 0.08774260673148353, 0.08240236322311853, 0.0773252622980482, 0.07250295995429872, 0.06792703729727498, 0.06358903128241358, 0.0594804637005589, 0.05559286838236543, 0.05191781660917574, 0.04844694072833519, 0.045171955980752694, 0.042084680557670666, 0.03917705391205346, 0.0364411533577275, 0.033869208996403666, 0.031453617018985, 0.029186951433115504, 0.027061974273770033, 0.025071644357838672, 0.023209124647140075, 0.0214677882871318, 0.01984122339079921, 0.018323236638827463, 0.016907855768226104, 0.015589331022117408, 0.014362135633453003, 0.013220965415025607, 0.012160737527331282, 0.011176588494649939, 0.010263871538186153, 0.009418153293284751, 0.008635209975644624, 0.007911023059133089, 0.007241774525289354, 0.006623841741928975, 0.006053792025457812, 0.005528376938598487, 0.005044526372259342, 0.004599342457255244, 0.004190093348552506, 0.003814206921672727, 0.00346926441787937, 0.0031529940717994523, 0.002863264752221866, 0.0025980796439755197, 0.0023555699960388587, 0.0021339889583780157, 0.0019317055274632945, 0.0017471986179802869, 0.0015790512759391155, 0.0014259450461971356, 0.0012866545053506402, 0.0011600419690211364, 0.0010450523807626958, 0.0009407083881481699, 0.0008461056100525355, 0.0007604080977391869, 0.0006828439910664701, 0.0006127013699641679, 0.0005493243002780956, 0.0004921090721417389, 0.0004405006282012008, 0.00039398917828905374, 0.00035210699650789463, 0.00031442539614041443, 0.0002805518773432896, 0.0002501274422019469, 0.00022282407141596062, 0.0001983423566452461, 0.00017640928236931793, 0.00015677615099052658, 0.00013921664484182239, 0.00012352501873527194, 0.00010951441670426968, 0.00009701530664553113, 0.000085874026652078, 0.00007595143694136696, 0.00006712167141956476, 0.00005927098308008699, 0.00005229667760850829, 0.00004610612975370932, 0.00004061587722377405);
11 |
12 | void main() {
13 |
14 | vec2 tex_offset = sample / textureSize(tex1, 0);
15 | vec3 result = texture(tex0, fragTexCoord).rgb;
16 | result += texture(tex1, fragTexCoord).rgb * gaussian[0];
17 | for (int i = 1; i < iterations; ++i) {
18 | result += texture(tex1, fragTexCoord + vec2(tex_offset.x * i, 0.0)).rgb * gaussian[int((100*i)/iterations)]*sample;
19 | result += texture(tex1, fragTexCoord - vec2(tex_offset.x * i, 0.0)).rgb * gaussian[int((100*i)/iterations)]*sample;
20 | }
21 | for (int i = 1; i < iterations; ++i) {
22 | result += texture(tex1, fragTexCoord + vec2(0.0, tex_offset.y * i)).rgb * gaussian[int((100*i)/iterations)]*sample;
23 | result += texture(tex1, fragTexCoord - vec2(0.0, tex_offset.y * i)).rgb * gaussian[int((100*i)/iterations)]*sample;
24 | }
25 | outputColor = vec4(result, 1.0);
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/shaders/build/postEffects/glow.vert:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | in vec2 vert;
4 | in vec2 texCoord;
5 | out vec2 fragTexCoord;
6 |
7 | void main() {
8 |
9 | gl_Position = vec4(vert, 0.0, 1.0);
10 | fragTexCoord = texCoord;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/shaders/diffuseSpecular.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | #include "./lib/base.glsl"
4 | #include "./lib/worldTransform.glsl"
5 | #include "./lib/textures.glsl"
6 | #include "./lib/fresnelEffect.glsl"
7 | #include "./lib/roughnessTexture.glsl"
8 | #include "./lib/ambientLight.glsl"
9 | #include "./lib/pointLights.glsl"
10 | #include "./lib/directionalLights.glsl"
11 | #include "./lib/indirectLight.glsl"
12 | #include "./lib/glowOutput.glsl"
13 |
14 | void main() {
15 | textures();
16 | roughnessTexture();
17 | glowOutput();
18 |
19 | #vert
20 | worldTransform();
21 | gl_Position = projection * camera * model * vec4(vert, 1);
22 | #endvert
23 |
24 | #frag
25 | if (unlit) {
26 | outputColor = diffuse;
27 | } else {
28 | vec4 aoDiffuse = ao * metalDiffuse;
29 | vec4 feSpecular = fresnelEffect(metalSpecular, normalValue);
30 | vec3 dLight = ambientLight(aoDiffuse) + pointLights(aoDiffuse, feSpecular, normalValue) + directionalLights(aoDiffuse, feSpecular, normalValue);
31 | vec3 iLight = indirectLight(aoDiffuse, feSpecular, normalValue);
32 | outputColor = vec4(dLight + iLight, diffuse.a);
33 | }
34 | #endfrag
35 | }
--------------------------------------------------------------------------------
/shaders/lib/ambientLight.glsl:
--------------------------------------------------------------------------------
1 | #frag
2 | #include "./base.glsl"
3 |
4 | uniform vec3 ambientLightValue;
5 |
6 | vec3 ambientLight(vec4 diffuse) {
7 | return ambientLightValue * diffuse.rgb;
8 | }
9 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/base.glsl:
--------------------------------------------------------------------------------
1 | uniform mat4 projection;
2 | uniform mat4 camera;
3 | uniform mat4 model;
4 | uniform mat4 modelNormal;
5 | uniform vec3 cameraTranslation;
6 |
7 | uniform bool unlit;
8 | uniform bool useTextures;
9 |
10 | #vert
11 | in vec3 vert;
12 | in vec3 normal;
13 | in vec2 texCoord;
14 | in vec4 color;
15 | #endvert
16 |
17 | #frag
18 | layout(location = 0) out vec4 outputColor;
19 | #endfrag
20 |
21 |
--------------------------------------------------------------------------------
/shaders/lib/common.glsl:
--------------------------------------------------------------------------------
1 |
2 | float pow2(float x) {
3 | return x*x;
4 | }
5 |
6 | float pow3(float x) {
7 | return x*x*x;
8 | }
--------------------------------------------------------------------------------
/shaders/lib/directLight.glsl:
--------------------------------------------------------------------------------
1 | #frag
2 | #include "./common.glsl"
3 | #include "./worldTransform.glsl"
4 |
5 | vec3 directLight( vec3 light, vec3 direction, vec4 diffuse, vec4 specular, vec4 normalValue ) {
6 | vec3 normal_tangentSpace = (normalValue.xyz*2) - 1;
7 | vec3 direction_tangentSpace = direction * TBNMatrix;
8 | vec3 eyeDirection_tangentSpace = eyeDirection * TBNMatrix;
9 | vec3 reflectedEye_tangentSpace = reflect( eyeDirection_tangentSpace, normal_tangentSpace );
10 |
11 | float diffuseMultiplier = max(0.0, dot(normal_tangentSpace, -direction_tangentSpace));
12 |
13 | float specularMultiplier = pow2(max(0.0, dot(reflectedEye_tangentSpace, -direction_tangentSpace)));
14 |
15 | vec3 color = (diffuseMultiplier * diffuse.rgb) + (specularMultiplier * specular.rgb);
16 |
17 | return color * light;
18 | }
19 | #endfrag
20 |
--------------------------------------------------------------------------------
/shaders/lib/directionalLights.glsl:
--------------------------------------------------------------------------------
1 | #frag
2 | #include "./base.glsl"
3 | #include "./worldTransform.glsl"
4 | #include "./directLight.glsl"
5 |
6 | #define MAX_DIRECTIONAL_LIGHTS 4
7 |
8 | uniform int nbDirectionalLights;
9 | uniform vec4 directionalLightVectors[ MAX_DIRECTIONAL_LIGHTS ];
10 | uniform vec4 directionalLightValues[ MAX_DIRECTIONAL_LIGHTS ];
11 |
12 | vec3 directionalLights(vec4 diffuse, vec4 specular, vec4 normalValue) {
13 | vec3 totalLight = vec3(0.0, 0.0, 0.0);
14 | for (int i=0; i < nbDirectionalLights; i++) {
15 | vec3 LightDirection = directionalLightVectors[i].rgb;
16 | vec3 LightValue = directionalLightValues[i].rgb;
17 |
18 | totalLight += directLight(LightValue, LightDirection, diffuse, specular, normalValue);
19 | }
20 | return totalLight;
21 | }
22 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/fresnelEffect.glsl:
--------------------------------------------------------------------------------
1 | #frag
2 | #include "./common.glsl"
3 | #include "./worldTransform.glsl"
4 |
5 | vec4 fresnelEffect(vec4 baseSpecular, vec4 normalValue) {
6 | vec3 normal_tangentSpace = (normalValue.xyz*2) - 1;
7 | vec3 normal_worldSpace = normal_tangentSpace * inverseTBNMatrix;
8 | float NdV = abs(dot(normal_worldSpace, eyeDirection));
9 |
10 | return mix(baseSpecular, vec4(1.0), pow(1.0 - NdV, 5.0));
11 | }
12 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/glowOutput.glsl:
--------------------------------------------------------------------------------
1 | #include "./base.glsl"
2 | #include "./textures.glsl"
3 |
4 | #frag
5 | uniform sampler2D glowMap;
6 |
7 | layout(location = 1) out vec4 brightColor;
8 | #endfrag
9 |
10 | #vert
11 | void glowOutput() {}
12 | #endvert
13 |
14 | #frag
15 | void glowOutput() {
16 | vec2 overflowTextCoord = repeatTextCoord();
17 |
18 | brightColor = fragColor * texture(glowMap, overflowTextCoord);
19 | }
20 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/indirectLight.glsl:
--------------------------------------------------------------------------------
1 | #frag
2 | #include "./worldTransform.glsl"
3 |
4 | uniform samplerCube environmentMap;
5 |
6 | vec3 indirectLight(vec4 diffuse, vec4 specular, vec4 normalValue) {
7 | vec3 normal_tangentSpace = (normalValue.xyz*2) - 1;
8 | vec3 normal_worldSpace = normal_tangentSpace * inverseTBNMatrix;
9 | vec3 reflectedEye_worldSpace = reflect( eyeDirection, normal_worldSpace );
10 |
11 | vec3 diffuseValue = textureLod(environmentMap, normal_worldSpace, 10).rgb;
12 | vec3 specularValue = textureLod(environmentMap, reflectedEye_worldSpace, roughness.r * 10).rgb;
13 |
14 | return (diffuse.rgb * diffuseValue) + (specular.rgb * specularValue);
15 | }
16 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/metalnessTexture.glsl:
--------------------------------------------------------------------------------
1 | #include "./textures.glsl"
2 |
3 | #vert
4 | void metalnessTexture() {}
5 | #endvert
6 |
7 | #frag
8 | uniform sampler2D metalnessMap;
9 |
10 | vec4 metalness;
11 | vec4 metalSpecular;
12 | vec4 metalDiffuse;
13 |
14 | void metalnessTexture() {
15 | vec2 overflowTextCoord = repeatTextCoord();
16 |
17 | metalness = texture(metalnessMap, overflowTextCoord);
18 | metalSpecular = mix(vec4(0.04), diffuse, metalness.r);
19 | metalDiffuse = mix(diffuse, vec4(0), metalness.r);
20 | }
21 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/pbrCompositeTextures.glsl:
--------------------------------------------------------------------------------
1 | #include "./textures.glsl"
2 |
3 | #vert
4 | void pbrCompositeTextures() {}
5 | #endvert
6 |
7 | #frag
8 | uniform sampler2D compositeMap;
9 |
10 | vec4 roughness;
11 | vec4 metalness;
12 | vec4 metalSpecular;
13 | vec4 metalDiffuse;
14 |
15 | void pbrCompositeTextures() {
16 | vec2 overflowTextCoord = repeatTextCoord();
17 |
18 | vec4 composite = texture(compositeMap, overflowTextCoord);
19 |
20 | ao = vec4(composite.r);
21 | roughness = vec4(composite.g);
22 | metalness = vec4(composite.b);
23 |
24 | metalSpecular = mix(vec4(0.04), diffuse, metalness.r);
25 | metalDiffuse = mix(diffuse, vec4(0), metalness.r);
26 | }
27 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/pointLights.glsl:
--------------------------------------------------------------------------------
1 | #frag
2 | #include "./base.glsl"
3 | #include "./worldTransform.glsl"
4 | #include "./directLight.glsl"
5 |
6 | #define MAX_POINT_LIGHTS 4
7 |
8 | uniform int nbPointLights;
9 | uniform vec4 pointLightPositions[ MAX_POINT_LIGHTS ];
10 | uniform vec4 pointLightValues[ MAX_POINT_LIGHTS ];
11 |
12 | vec3 pointLights(vec4 diffuse, vec4 specular, vec4 normalValue) {
13 | vec3 totalLight = vec3(0.0, 0.0, 0.0);
14 | for (int i=0; i < nbPointLights; i++) {
15 | vec3 LightPos = pointLightPositions[i].rgb;
16 | vec3 LightValue = pointLightValues[i].rgb;
17 |
18 | vec3 v = worldVertex - LightPos;
19 | float lightDistance = dot(v, v);
20 | float brightness = 1.0 / lightDistance;
21 |
22 | vec3 worldLightDir = normalize(v);
23 | vec3 light = brightness*LightValue;
24 |
25 | totalLight += directLight(light, worldLightDir, diffuse, specular, normalValue);
26 | }
27 | return totalLight;
28 | }
29 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/roughnessTexture.glsl:
--------------------------------------------------------------------------------
1 | #include "./textures.glsl"
2 |
3 | #vert
4 | void roughnessTexture() {}
5 | #endvert
6 |
7 | #frag
8 | uniform sampler2D roughnessMap;
9 |
10 | vec4 roughness;
11 |
12 | void roughnessTexture() {
13 | vec2 overflowTextCoord = repeatTextCoord();
14 |
15 | roughness = texture(roughnessMap, overflowTextCoord);
16 | }
17 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/textures.glsl:
--------------------------------------------------------------------------------
1 | #include "./base.glsl"
2 |
3 | #vert
4 | out vec2 fragTexCoord;
5 | out vec4 fragColor;
6 |
7 | void textures() {
8 | fragTexCoord = texCoord;
9 | fragColor = color;
10 | }
11 | #endvert
12 |
13 | #frag
14 | uniform sampler2D normalMap;
15 | uniform sampler2D diffuseMap;
16 | uniform sampler2D specularMap;
17 | uniform sampler2D aoMap;
18 |
19 | in vec2 fragTexCoord;
20 | in vec4 fragColor;
21 |
22 | vec4 normalValue;
23 | vec4 diffuse;
24 | vec4 specular;
25 | vec4 ao;
26 |
27 | vec2 repeatTextCoord() {
28 | float textureX = fragTexCoord.x - int(fragTexCoord.x);
29 | float textureY = fragTexCoord.y - int(fragTexCoord.y);
30 | if (fragTexCoord.x < 0) {textureX = textureX + 1.0;}
31 | if (fragTexCoord.y < 0) {textureY = textureY + 1.0;}
32 | return vec2(textureX, textureY);
33 | }
34 |
35 | void textures() {
36 | vec2 overflowTextCoord = repeatTextCoord();
37 |
38 | // multiply color by diffuse map. use only color if no map is provided
39 | if (useTextures) {
40 | diffuse = fragColor * texture(diffuseMap, overflowTextCoord);
41 | specular = texture(specularMap, overflowTextCoord);
42 | normalValue = texture(normalMap, overflowTextCoord);
43 | ao = texture(aoMap, overflowTextCoord);
44 | } else {
45 | diffuse = fragColor;
46 | specular = vec4(0);
47 | normalValue = vec4(0);
48 | ao = vec4(1);
49 | }
50 | }
51 | #endfrag
--------------------------------------------------------------------------------
/shaders/lib/worldTransform.glsl:
--------------------------------------------------------------------------------
1 | #include "./base.glsl"
2 |
3 | #frag
4 | in vec3 worldVertex;
5 | in vec3 worldNormal;
6 | in vec3 eyeDirection;
7 | in mat3 TBNMatrix;
8 | in mat3 inverseTBNMatrix;
9 | #endfrag
10 |
11 | #vert
12 | out vec3 worldVertex;
13 | out vec3 worldNormal;
14 | out vec3 eyeDirection;
15 | out mat3 TBNMatrix;
16 | out mat3 inverseTBNMatrix;
17 |
18 | void worldTransform() {
19 | worldVertex = (model * vec4(vert,1)).xyz;
20 | worldNormal = (modelNormal * vec4(normal,1)).xyz;
21 | worldNormal = normalize(worldNormal);
22 | eyeDirection = normalize(worldVertex - cameraTranslation);
23 |
24 | // generate arbitrary tangent and bitangent to the normal
25 | vec3 tangent = cross(normal, normal + vec3(-1));
26 | vec3 bitangent = cross(normal, tangent);
27 | vec3 worldTangent = normalize((modelNormal * vec4(tangent,1)).xyz);
28 | vec3 worldBitangent = normalize((modelNormal * vec4(bitangent,1)).xyz);
29 |
30 | //tangent space conversion - worldToTangent
31 | TBNMatrix = mat3(worldTangent, worldBitangent, worldNormal);
32 | inverseTBNMatrix = inverse(TBNMatrix);
33 | }
34 | #endvert
--------------------------------------------------------------------------------
/shaders/pbr.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | #include "./lib/base.glsl"
4 | #include "./lib/worldTransform.glsl"
5 | #include "./lib/textures.glsl"
6 | #include "./lib/metalnessTexture.glsl"
7 | #include "./lib/roughnessTexture.glsl"
8 | #include "./lib/fresnelEffect.glsl"
9 | #include "./lib/ambientLight.glsl"
10 | #include "./lib/pointLights.glsl"
11 | #include "./lib/directionalLights.glsl"
12 | #include "./lib/indirectLight.glsl"
13 | #include "./lib/glowOutput.glsl"
14 |
15 | void main() {
16 | textures();
17 | metalnessTexture();
18 | roughnessTexture();
19 | glowOutput();
20 |
21 | #vert
22 | worldTransform();
23 | gl_Position = projection * camera * model * vec4(vert, 1);
24 | #endvert
25 |
26 | #frag
27 | if (unlit) {
28 | outputColor = diffuse;
29 | } else {
30 | vec4 aoDiffuse = ao * metalDiffuse;
31 | vec4 feSpecular = fresnelEffect(metalSpecular, normalValue);
32 | vec3 dLight = ambientLight(aoDiffuse) + pointLights(aoDiffuse, feSpecular, normalValue) + directionalLights(aoDiffuse, feSpecular, normalValue);
33 | vec3 iLight = indirectLight(aoDiffuse, feSpecular, normalValue);
34 | outputColor = vec4(dLight + iLight, diffuse.a);
35 | }
36 | #endfrag
37 | }
--------------------------------------------------------------------------------
/shaders/pbrComposite.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | #include "./lib/base.glsl"
4 | #include "./lib/worldTransform.glsl"
5 | #include "./lib/textures.glsl"
6 | #include "./lib/pbrCompositeTextures.glsl"
7 | #include "./lib/fresnelEffect.glsl"
8 | #include "./lib/ambientLight.glsl"
9 | #include "./lib/pointLights.glsl"
10 | #include "./lib/directionalLights.glsl"
11 | #include "./lib/indirectLight.glsl"
12 | #include "./lib/glowOutput.glsl"
13 |
14 | void main() {
15 | textures();
16 | pbrCompositeTextures();
17 | glowOutput();
18 |
19 | #vert
20 | worldTransform();
21 | gl_Position = projection * camera * model * vec4(vert, 1);
22 | #endvert
23 |
24 | #frag
25 | if (unlit) {
26 | outputColor = diffuse;
27 | } else {
28 | vec4 aoDiffuse = ao * metalDiffuse;
29 | vec4 feSpecular = fresnelEffect(metalSpecular, normalValue);
30 | vec3 dLight = ambientLight(aoDiffuse) + pointLights(aoDiffuse, feSpecular, normalValue) + directionalLights(aoDiffuse, feSpecular, normalValue);
31 | vec3 iLight = indirectLight(aoDiffuse, feSpecular, normalValue);
32 | outputColor = vec4(dLight + iLight, diffuse.a);
33 | }
34 | #endfrag
35 | }
--------------------------------------------------------------------------------
/shaders/postEffects/cell.glsl:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | #vert
4 | in vec2 vert;
5 | in vec2 texCoord;
6 | out vec2 fragTexCoord;
7 | #endvert
8 |
9 | #frag
10 | uniform sampler2D tex0;
11 | in vec2 fragTexCoord;
12 | out vec4 outputColor;
13 |
14 | float cell( float source ) {
15 | if (source < 0.3) {
16 | return 0;
17 | } else if (source < 0.6) {
18 | return 0.3;
19 | } else if (source < 0.9) {
20 | return 0.6;
21 | }
22 | return 1;
23 | }
24 | #endfrag
25 |
26 | void main() {
27 | #vert
28 | gl_Position = vec4(vert, 0.0, 1.0);
29 | fragTexCoord = texCoord;
30 | #endvert
31 |
32 | #frag
33 | vec4 finalColor = vec4(0,0,0,1);
34 | vec4 source = texture(tex0, fragTexCoord);
35 | finalColor = vec4( cell(source.r), cell(source.g), cell(source.b), cell(source.a) );
36 | outputColor = finalColor;
37 | #endfrag
38 | }
--------------------------------------------------------------------------------
/shaders/postEffects/glow.glsl:
--------------------------------------------------------------------------------
1 | #version 330
2 |
3 | #vert
4 | in vec2 vert;
5 | in vec2 texCoord;
6 | out vec2 fragTexCoord;
7 | #endvert
8 |
9 | #frag
10 | uniform sampler2D tex0;
11 | uniform sampler2D tex1;
12 | in vec2 fragTexCoord;
13 | out vec4 outputColor;
14 |
15 | uniform float sample = 2.0;
16 | uniform float iterations = 8;
17 | uniform #lookup 100 gaussian (2.718 ^ ( -(i*0.02 + 1.2) ^ 2.0 ));
18 | #endfrag
19 |
20 | void main() {
21 | #vert
22 | gl_Position = vec4(vert, 0.0, 1.0);
23 | fragTexCoord = texCoord;
24 | #endvert
25 |
26 | #frag
27 | vec2 tex_offset = sample / textureSize(tex1, 0);
28 | vec3 result = texture(tex0, fragTexCoord).rgb;
29 | result += texture(tex1, fragTexCoord).rgb * gaussian[0];
30 | for (int i = 1; i < iterations; ++i) {
31 | result += texture(tex1, fragTexCoord + vec2(tex_offset.x * i, 0.0)).rgb * gaussian[int((100*i)/iterations)]*sample;
32 | result += texture(tex1, fragTexCoord - vec2(tex_offset.x * i, 0.0)).rgb * gaussian[int((100*i)/iterations)]*sample;
33 | }
34 | for (int i = 1; i < iterations; ++i) {
35 | result += texture(tex1, fragTexCoord + vec2(0.0, tex_offset.y * i)).rgb * gaussian[int((100*i)/iterations)]*sample;
36 | result += texture(tex1, fragTexCoord - vec2(0.0, tex_offset.y * i)).rgb * gaussian[int((100*i)/iterations)]*sample;
37 | }
38 | outputColor = vec4(result, 1.0);
39 | #endfrag
40 | }
--------------------------------------------------------------------------------
/ui/children.go:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | type Children []Element
4 |
5 | func (c Children) GetChild(index int) Element {
6 | if index >= len(c) {
7 | return nil
8 | }
9 | return c[index]
10 | }
11 |
12 | func (c Children) GetChildById(id string) Element {
13 | for _, child := range c {
14 | if child.GetId() == id {
15 | return child
16 | }
17 | if elem := child.GetChildren().GetChildById(id); elem != nil {
18 | return elem
19 | }
20 | }
21 | return nil
22 | }
23 |
24 | func (c Children) TextElementById(id string) *TextElement {
25 | elem := c.GetChildById(id)
26 | if elem != nil && len(elem.GetChildren()) > 0 {
27 | if textElement, ok := elem.GetChildren().GetChild(0).(*TextElement); ok {
28 | return textElement
29 | }
30 | if textField, ok := elem.GetChildren().GetChild(0).(*TextField); ok {
31 | return textField.text
32 | }
33 | }
34 | return nil
35 | }
36 |
--------------------------------------------------------------------------------
/ui/element.go:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/renderer"
6 | )
7 |
8 | type Element interface {
9 | Render(size, offset mgl32.Vec2) mgl32.Vec2
10 | ReRender()
11 | Spatial() (spatial renderer.Spatial)
12 | GlobalOrthoOrder() int
13 | GetId() string
14 | SetId(id string)
15 | GetChildren() Children
16 | mouseMove(position mgl32.Vec2) bool
17 | mouseClick(button int, release bool, position mgl32.Vec2) bool
18 | keyClick(key string, release bool)
19 | }
20 |
21 | // Sort elements
22 | type byGlobalOrthoOrder []Element
23 |
24 | func (slice byGlobalOrthoOrder) Len() int {
25 | return len(slice)
26 | }
27 |
28 | func (slice byGlobalOrthoOrder) Less(i, j int) bool {
29 | return slice[i].GlobalOrthoOrder() > slice[j].GlobalOrthoOrder()
30 | }
31 |
32 | func (slice byGlobalOrthoOrder) Swap(i, j int) {
33 | slice[i], slice[j] = slice[j], slice[i]
34 | }
35 |
--------------------------------------------------------------------------------
/ui/hitbox.go:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/emitter"
6 | "github.com/walesey/go-engine/util"
7 | )
8 |
9 | type Hitbox interface {
10 | AddOnClick(handler func(button int, release bool, position mgl32.Vec2))
11 | AddOnHover(handler func())
12 | AddOnUnHover(handler func())
13 | AddOnMouseMove(handler func(position mgl32.Vec2))
14 | MouseMove(position mgl32.Vec2) bool
15 | MouseClick(button int, release bool, position mgl32.Vec2) bool
16 | SetSize(size mgl32.Vec2)
17 | }
18 |
19 | type HitboxImpl struct {
20 | size mgl32.Vec2
21 | events emitter.EventEmitter
22 | hoverState bool
23 | }
24 |
25 | type clickEvent struct {
26 | button int
27 | release bool
28 | position mgl32.Vec2
29 | }
30 |
31 | func (hb *HitboxImpl) AddOnClick(handler func(button int, release bool, position mgl32.Vec2)) {
32 | hb.events.On("click", func(e emitter.Event) {
33 | if ce, ok := e.(clickEvent); ok {
34 | handler(ce.button, ce.release, ce.position)
35 | }
36 | })
37 | }
38 |
39 | func (hb *HitboxImpl) AddOnHover(handler func()) {
40 | hb.events.On("hover", func(e emitter.Event) {
41 | handler()
42 | })
43 | }
44 |
45 | func (hb *HitboxImpl) AddOnUnHover(handler func()) {
46 | hb.events.On("unHover", func(e emitter.Event) {
47 | handler()
48 | })
49 | }
50 |
51 | func (hb *HitboxImpl) AddOnMouseMove(handler func(position mgl32.Vec2)) {
52 | hb.events.On("mouseMove", func(e emitter.Event) {
53 | if pos, ok := e.(mgl32.Vec2); ok {
54 | handler(pos)
55 | }
56 | })
57 | }
58 |
59 | func (hb *HitboxImpl) MouseMove(position mgl32.Vec2) bool {
60 | if util.PointLiesInsideAABB(mgl32.Vec2{}, hb.size, position) {
61 | if !hb.hoverState {
62 | hb.hoverState = true
63 | hb.events.Do("hover", position)
64 | }
65 | hb.events.Do("mouseMove", position)
66 | return true
67 | } else if hb.hoverState {
68 | hb.hoverState = false
69 | hb.events.Do("unHover", position)
70 | }
71 | return false
72 | }
73 |
74 | func (hb *HitboxImpl) MouseClick(button int, release bool, position mgl32.Vec2) bool {
75 | if util.PointLiesInsideAABB(mgl32.Vec2{}, hb.size, position) {
76 | hb.events.Do("click", clickEvent{button, release, position})
77 | return true
78 | }
79 | return false
80 | }
81 |
82 | func (hb *HitboxImpl) SetSize(size mgl32.Vec2) {
83 | hb.size = size
84 | }
85 |
86 | func NewHitbox() Hitbox {
87 | return &HitboxImpl{
88 | events: emitter.New(1),
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/ui/htmlAssets.go:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import (
4 | "image"
5 |
6 | "github.com/walesey/go-engine/libs/freetype/truetype"
7 | )
8 |
9 | type HtmlAssets struct {
10 | fontMap map[string]*truetype.Font
11 | callbackMap map[string]func(element Element, args ...interface{})
12 | imageMap map[string]image.Image
13 | }
14 |
15 | func (assets HtmlAssets) AddFont(key string, font *truetype.Font) {
16 | assets.fontMap[key] = font
17 | }
18 |
19 | func (assets HtmlAssets) AddCallback(key string, callback func(element Element, args ...interface{})) {
20 | assets.callbackMap[key] = callback
21 | }
22 |
23 | func (assets HtmlAssets) AddImage(key string, img image.Image) {
24 | assets.imageMap[key] = img
25 | }
26 |
27 | func NewHtmlAssets() HtmlAssets {
28 | assets := HtmlAssets{
29 | fontMap: make(map[string]*truetype.Font),
30 | callbackMap: make(map[string]func(element Element, args ...interface{})),
31 | imageMap: make(map[string]image.Image),
32 | }
33 | defaultFont, err := LoadFont(getDefaultFont())
34 | if err == nil {
35 | assets.AddFont("default", defaultFont)
36 | }
37 | return assets
38 | }
39 |
--------------------------------------------------------------------------------
/ui/image.go:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import (
4 | "image"
5 |
6 | "github.com/go-gl/mathgl/mgl32"
7 | "github.com/walesey/go-engine/renderer"
8 | )
9 |
10 | type ImageElement struct {
11 | id string
12 | Hitbox Hitbox
13 | percentWidth bool
14 | percentHeight bool
15 | width, height float32
16 | rotation float32
17 | size, offset mgl32.Vec2
18 | node *renderer.Node
19 | img image.Image
20 | }
21 |
22 | func (ie *ImageElement) Render(size, offset mgl32.Vec2) mgl32.Vec2 {
23 | ie.size, ie.offset = size, offset
24 | width, height := ie.getWidth(size.X()), ie.getHeight(size.X())
25 | if ie.img != nil {
26 | if width <= 0 && height <= 0 {
27 | width = float32(ie.img.Bounds().Size().X)
28 | height = float32(ie.img.Bounds().Size().Y)
29 | } else if width <= 0 {
30 | width = height * float32(ie.img.Bounds().Size().X) / float32(ie.img.Bounds().Size().Y)
31 | } else if height <= 0 {
32 | height = width * float32(ie.img.Bounds().Size().Y) / float32(ie.img.Bounds().Size().X)
33 | }
34 | }
35 | imgSize := mgl32.Vec2{width, height}
36 | ie.node.SetScale(imgSize.Vec3(0))
37 | ie.node.SetTranslation(offset.Vec3(0))
38 | ie.offset = offset
39 | ie.Hitbox.SetSize(imgSize)
40 | return imgSize
41 | }
42 |
43 | func (ie *ImageElement) ReRender() {
44 | ie.Render(ie.size, ie.offset)
45 | }
46 |
47 | func (ie *ImageElement) Spatial() renderer.Spatial {
48 | return ie.node
49 | }
50 |
51 | func (ie *ImageElement) GlobalOrthoOrder() int {
52 | return 0
53 | }
54 |
55 | func (ie *ImageElement) GetId() string {
56 | return ie.id
57 | }
58 |
59 | func (ie *ImageElement) SetId(id string) {
60 | ie.id = id
61 | }
62 |
63 | func (ie *ImageElement) GetChildren() Children {
64 | return []Element{}
65 | }
66 |
67 | func (ie *ImageElement) SetWidth(width float32) {
68 | ie.width = width
69 | }
70 |
71 | func (ie *ImageElement) UsePercentWidth(usePercent bool) {
72 | ie.percentWidth = usePercent
73 | }
74 |
75 | func (ie *ImageElement) SetHeight(height float32) {
76 | ie.height = height
77 | }
78 |
79 | func (ie *ImageElement) UsePercentHeight(usePercent bool) {
80 | ie.percentHeight = usePercent
81 | }
82 |
83 | func (ie *ImageElement) getWidth(parentWidth float32) float32 {
84 | if ie.percentWidth {
85 | return parentWidth * ie.width / 100.0
86 | }
87 | return ie.width
88 | }
89 |
90 | func (ie *ImageElement) getHeight(parentWidth float32) float32 {
91 | if ie.percentHeight {
92 | return parentWidth * ie.height / 100.0
93 | }
94 | return ie.height
95 | }
96 |
97 | func (ie *ImageElement) SetRotation(rotation float32) {
98 | ie.rotation = rotation
99 | }
100 |
101 | func (ie *ImageElement) SetImage(img image.Image) {
102 | mat := renderer.NewMaterial(renderer.NewTexture("diffuseMap", img, false))
103 | ie.node.Material = mat
104 | ie.img = img
105 | }
106 |
107 | func (ie *ImageElement) mouseMove(position mgl32.Vec2) bool {
108 | offsetPos := position.Sub(ie.offset)
109 | return ie.Hitbox.MouseMove(offsetPos)
110 | }
111 |
112 | func (ie *ImageElement) mouseClick(button int, release bool, position mgl32.Vec2) bool {
113 | offsetPos := position.Sub(ie.offset)
114 | return ie.Hitbox.MouseClick(button, release, offsetPos)
115 | }
116 |
117 | func (ie *ImageElement) keyClick(key string, release bool) {}
118 |
119 | func NewImageElement(img image.Image) *ImageElement {
120 | imageElement := &ImageElement{
121 | rotation: 0,
122 | Hitbox: NewHitbox(),
123 | node: renderer.NewNode(),
124 | }
125 | box := renderer.CreateBoxWithOffset(1, 1, 0, 0)
126 | imageElement.node.Add(box)
127 | imageElement.SetImage(img)
128 | return imageElement
129 | }
130 |
--------------------------------------------------------------------------------
/ui/progressBar.go:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import (
4 | "fmt"
5 |
6 | "image/color"
7 |
8 | "github.com/go-gl/mathgl/mgl32"
9 | )
10 |
11 | const nbBars = 20
12 |
13 | func NewProgressBar(label string) *Window {
14 | window := NewWindow()
15 | window.SetTranslation(mgl32.Vec3{500, 200, 0})
16 | window.SetScale(mgl32.Vec3{325, 0, 1})
17 |
18 | container := NewContainer()
19 | window.SetElement(container)
20 | container.SetBackgroundColor(200, 200, 200, 255)
21 | container.SetWidth(325)
22 |
23 | labelElem := NewTextElement(label, color.RGBA{10, 10, 20, 255}, 12, nil)
24 | labelContainer := NewContainer()
25 | labelContainer.SetWidth(100)
26 | labelContainer.UsePercentWidth(true)
27 | labelContainer.SetMargin(NewMargin(5))
28 | labelContainer.AddChildren(labelElem)
29 |
30 | progressBar := NewContainer()
31 | progressBar.SetBackgroundColor(50, 50, 50, 255)
32 | progressBar.SetMargin(NewMargin(5))
33 | progressBar.SetPadding(Margin{0, 0, 0, 5})
34 | progressBar.SetHeight(40)
35 | progressBar.SetWidth(315)
36 |
37 | container.AddChildren(labelContainer, progressBar)
38 |
39 | for i := 1; i <= nbBars; i++ {
40 | box := NewContainer()
41 | progressBar.AddChildren(box)
42 | box.SetId(fmt.Sprintf("progress%v", i))
43 | box.SetBackgroundColor(50, 220, 80, 255)
44 | box.SetMargin(Margin{10, 0, 10, 5})
45 | box.SetHeight(20)
46 | box.SetWidth(10)
47 | }
48 |
49 | window.Render()
50 |
51 | return window
52 | }
53 |
54 | func SetProgressBar(pb *Window, progress int) {
55 | for i := 1; i <= nbBars; i++ {
56 | container, ok := pb.ElementById(fmt.Sprintf("progress%v", i)).(*Container)
57 | if ok {
58 | if i > progress {
59 | container.SetBackgroundColor(0, 0, 0, 0)
60 | } else {
61 | container.SetBackgroundColor(0, 255, 0, 255)
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/ui/window.go:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import (
4 | "image/color"
5 |
6 | "github.com/go-gl/mathgl/mgl32"
7 | "github.com/walesey/go-engine/renderer"
8 | )
9 |
10 | var activeWindow *Window
11 |
12 | type Activatable interface {
13 | Active() bool
14 | Activate()
15 | Deactivate()
16 | }
17 |
18 | type Window struct {
19 | node, elementNode, background *renderer.Node
20 | backgroundBox *renderer.Geometry
21 | element Element
22 | size, position mgl32.Vec2
23 | mousePos mgl32.Vec2
24 | Tabs []Activatable
25 | }
26 |
27 | func (w *Window) Draw(renderer renderer.Renderer, transform mgl32.Mat4) {
28 | w.node.Draw(renderer, transform)
29 | }
30 |
31 | func (w *Window) Destroy(renderer renderer.Renderer) {
32 | w.node.Destroy(renderer)
33 | }
34 |
35 | func (w *Window) Center() mgl32.Vec3 {
36 | return w.node.Center()
37 | }
38 |
39 | func (w *Window) SetParent(parent *renderer.Node) {
40 | w.node.SetParent(parent)
41 | }
42 |
43 | func (w *Window) Optimize(geometry *renderer.Geometry, transform mgl32.Mat4) {
44 | w.node.Optimize(geometry, transform)
45 | }
46 |
47 | func (w *Window) BoundingRadius() float32 {
48 | return w.node.BoundingRadius()
49 | }
50 |
51 | func (w *Window) OrthoOrder() int {
52 | return w.node.OrthoOrder()
53 | }
54 |
55 | func (w *Window) SetScale(scale mgl32.Vec3) {
56 | w.background.SetScale(scale)
57 | w.size = scale.Vec2()
58 | w.Render()
59 | }
60 |
61 | func (w *Window) SetTranslation(translation mgl32.Vec3) {
62 | w.node.SetTranslation(translation)
63 | w.position = translation.Vec2()
64 | }
65 |
66 | func (w *Window) SetOrientation(orientation mgl32.Quat) {
67 | w.node.SetOrientation(orientation)
68 | }
69 |
70 | func (w *Window) SetBackgroundColor(r, g, b, a uint8) {
71 | w.backgroundBox.SetColor(color.NRGBA{r, g, b, a})
72 | }
73 |
74 | func (w *Window) SetElement(element Element) {
75 | if w.element != nil {
76 | w.elementNode.Remove(w.element.Spatial(), true)
77 | }
78 | w.element = element
79 | w.elementNode.Add(element.Spatial())
80 | w.Render()
81 | }
82 |
83 | func (w *Window) Render() {
84 | if w.element != nil {
85 | size := w.element.Render(w.size, mgl32.Vec2{0, 0})
86 | width, height := w.size.X(), w.size.Y()
87 | if size.X() > width {
88 | width = size.X()
89 | }
90 | if size.Y() > height {
91 | height = size.Y()
92 | }
93 | w.background.SetScale(mgl32.Vec2{width, height}.Vec3(0))
94 | }
95 | }
96 |
97 | func (w *Window) ElementById(id string) Element {
98 | if w.element != nil {
99 | if w.element.GetId() == id {
100 | return w.element
101 | }
102 | return w.element.GetChildren().GetChildById(id)
103 | }
104 | return nil
105 | }
106 |
107 | func (w *Window) TextElementById(id string) *TextElement {
108 | if w.element != nil {
109 | return w.element.GetChildren().TextElementById(id)
110 | }
111 | return nil
112 | }
113 |
114 | func (w *Window) mouseMove(position mgl32.Vec2) {
115 | w.mousePos = position.Sub(w.position)
116 | if w.element != nil {
117 | w.element.mouseMove(w.mousePos)
118 | }
119 | }
120 |
121 | func (w *Window) mouseClick(button int, release bool) {
122 | if w.element != nil {
123 | // Deselect all fields
124 | if !release {
125 | deactivateAllFields(w.element)
126 | }
127 | // Process the click
128 | if w.element.mouseClick(button, release, w.mousePos) {
129 | // set this to the active window
130 | if !release {
131 | if activeWindow != nil {
132 | activeWindow.node.OrthoOrderValue = 10
133 | }
134 | w.node.OrthoOrderValue = 100
135 | activeWindow = w
136 | }
137 | }
138 | }
139 | }
140 |
141 | func (w *Window) keyClick(key string, release bool) {
142 | if w.element != nil {
143 | w.element.keyClick(key, release)
144 | }
145 | }
146 |
147 | func deactivateAllFields(elem Element) {
148 | for _, child := range elem.GetChildren() {
149 | deactivateAllFields(child)
150 | switch t := child.(type) {
151 | case *TextField:
152 | t.Deactivate()
153 | case *Dropdown:
154 | t.deactivate_setFlag()
155 | }
156 | }
157 | }
158 |
159 | func NewWindow() *Window {
160 | node := renderer.NewNode()
161 | elementNode := renderer.NewNode()
162 | background := renderer.NewNode()
163 | background.Material = renderer.NewMaterial()
164 | box := renderer.CreateBoxWithOffset(1, 1, 0, 0)
165 | box.SetColor(color.NRGBA{255, 255, 255, 255})
166 | background.Add(box)
167 | node.Add(background)
168 | node.Add(elementNode)
169 | node.OrthoOrderValue = 10
170 | return &Window{
171 | node: node,
172 | backgroundBox: box,
173 | background: background,
174 | elementNode: elementNode,
175 | size: mgl32.Vec2{500, 1},
176 | Tabs: []Activatable{},
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/ui/windowAddons.go:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import (
4 | "github.com/go-gl/mathgl/mgl32"
5 | "github.com/walesey/go-engine/controller"
6 | )
7 |
8 | func ClickAndDragWindow(window *Window, hitbox Hitbox, c controller.Controller) {
9 | grabbed := false
10 | grabOffset := mgl32.Vec2{}
11 | hitbox.AddOnClick(func(button int, release bool, position mgl32.Vec2) {
12 | grabOffset = position
13 | grabbed = !release
14 | })
15 | c.BindMouseAction(func() {
16 | grabbed = false
17 | }, controller.MouseButton1, controller.Release)
18 | c.BindAxisAction(func(xpos, ypos float32) {
19 | if grabbed {
20 | position := mgl32.Vec2{xpos, ypos}
21 | window.SetTranslation(position.Sub(grabOffset).Vec3(0))
22 | }
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/util/image.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "image"
4 |
5 | // ImageColor - returns an image with a single pixel
6 | func ImageColor(r, g, b, a uint8) image.Image {
7 | return &image.RGBA{
8 | Pix: []uint8{r, g, b, a},
9 | Stride: 4,
10 | Rect: image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{1, 1}},
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/util/serialize_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "testing"
5 |
6 | "bytes"
7 |
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestUint64(t *testing.T) {
12 | var i uint64 = 50
13 | data, err := SerializeArgs(i)
14 | assert.NoError(t, err)
15 | result, err := UInt64frombytes(bytes.NewBuffer(data))
16 | assert.NoError(t, err)
17 | assert.EqualValues(t, i, result)
18 | }
19 |
20 | func TestAbc(t *testing.T) {
21 | a := []string{"a", "b"}
22 | data, err := SerializeArgs(len(a))
23 | assert.NoError(t, err)
24 | result, err := UInt32frombytes(bytes.NewBuffer(data))
25 | assert.NoError(t, err)
26 | assert.EqualValues(t, 2, result)
27 | }
28 |
--------------------------------------------------------------------------------
/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "encoding/base64"
5 | "fmt"
6 | "io/ioutil"
7 | "strings"
8 | "time"
9 | )
10 |
11 | func Base64ToBytes(base64String string) []byte {
12 | decoder := base64.NewDecoder(base64.URLEncoding, strings.NewReader(base64String))
13 | data, err := ioutil.ReadAll(decoder)
14 | if err != nil {
15 | fmt.Printf("Error converting base64 string to bytes: %v\n", err)
16 | return []byte{}
17 | }
18 | return data
19 | }
20 |
21 | func SetInterval(fn func(), interval time.Duration) func() {
22 | ticker := time.NewTicker(interval)
23 | quit := make(chan struct{})
24 | go func() {
25 | for {
26 | select {
27 | case <-ticker.C:
28 | fn()
29 | case <-quit:
30 | ticker.Stop()
31 | return
32 | }
33 | }
34 | }()
35 | return func() {
36 | close(quit)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------