Compare commits

...

8 Commits

17 changed files with 319 additions and 194 deletions

View File

@ -5,23 +5,28 @@ var (
TexturePaths map[string]uint32 = make(map[string]uint32) TexturePaths map[string]uint32 = make(map[string]uint32)
) )
func SetTexture(t Texture) { func AddTextureToCache(t Texture) {
if _, ok := TexturePaths[t.Path]; ok { if t.Path != "" {
if _, ok := TexturePaths[t.Path]; ok {
return
}
println("Loaded texture from path:", t.Path)
Textures[t.TexID] = t
TexturePaths[t.Path] = t.TexID
return return
} }
println("Loaded texture:", t.Path) println("Loaded in-mem texture with ID:", t.TexID)
Textures[t.TexID] = t
TexturePaths[t.Path] = t.TexID TexturePaths[t.Path] = t.TexID
} }
func GetTexture(texID uint32) (Texture, bool) { func GetTextureFromCacheID(texID uint32) (Texture, bool) {
tex, ok := Textures[texID] tex, ok := Textures[texID]
return tex, ok return tex, ok
} }
func GetTexturePath(path string) (Texture, bool) { func GetTextureFromCachePath(path string) (Texture, bool) {
tex, ok := Textures[TexturePaths[path]] tex, ok := Textures[TexturePaths[path]]
return tex, ok return tex, ok
} }

View File

@ -2,6 +2,7 @@ package assets
import ( import (
"bytes" "bytes"
"image"
"image/color" "image/color"
"image/png" "image/png"
"os" "os"
@ -17,6 +18,7 @@ const (
) )
type Texture struct { type Texture struct {
//Path only exists for textures loaded from disk
Path string Path string
TexID uint32 TexID uint32
Width int32 Width int32
@ -24,10 +26,22 @@ type Texture struct {
Pixels []byte Pixels []byte
} }
func LoadPNGTexture(file string) (Texture, error) { type TextureLoadOptions struct {
TryLoadFromCache bool
WriteToCache bool
GenMipMaps bool
}
if tex, ok := GetTexturePath(file); ok { func LoadTexturePNG(file string, loadOptions *TextureLoadOptions) (Texture, error) {
return tex, nil
if loadOptions == nil {
loadOptions = &TextureLoadOptions{}
}
if loadOptions.TryLoadFromCache {
if tex, ok := GetTextureFromCachePath(file); ok {
return tex, nil
}
} }
//Load from disk //Load from disk
@ -72,14 +86,72 @@ func LoadPNGTexture(file string) (Texture, error) {
// set the texture wrapping/filtering options (on the currently bound texture object) // set the texture wrapping/filtering options (on the currently bound texture object)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
// load and generate the texture // load and generate the texture
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, tex.Width, tex.Height, 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&tex.Pixels[0])) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, tex.Width, tex.Height, 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&tex.Pixels[0]))
gl.GenerateMipmap(gl.TEXTURE_2D)
SetTexture(tex) if loadOptions.GenMipMaps {
gl.GenerateMipmap(tex.TexID)
}
if loadOptions.WriteToCache {
AddTextureToCache(tex)
}
return tex, nil
}
func LoadTextureInMemImg(img image.Image, loadOptions *TextureLoadOptions) (Texture, error) {
if loadOptions == nil {
loadOptions = &TextureLoadOptions{}
}
tex := Texture{
Width: int32(img.Bounds().Dx()),
Height: int32(img.Bounds().Dy()),
Pixels: make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4),
}
//NOTE: Load bottom left to top right because this is the texture coordinate system used by OpenGL
//NOTE: We only support 8-bit channels (32-bit colors) for now
i := 0
for y := img.Bounds().Dy() - 1; y >= 0; y-- {
for x := 0; x < img.Bounds().Dx(); x++ {
c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA)
tex.Pixels[i] = c.R
tex.Pixels[i+1] = c.G
tex.Pixels[i+2] = c.B
tex.Pixels[i+3] = c.A
i += 4
}
}
//Prepare opengl stuff
gl.GenTextures(1, &tex.TexID)
gl.BindTexture(gl.TEXTURE_2D, tex.TexID)
// set the texture wrapping/filtering options (on the currently bound texture object)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
// load and generate the texture
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, tex.Width, tex.Height, 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&tex.Pixels[0]))
if loadOptions.GenMipMaps {
gl.GenerateMipmap(tex.TexID)
}
if loadOptions.WriteToCache {
AddTextureToCache(tex)
}
return tex, nil return tex, nil
} }

View File

@ -30,7 +30,12 @@ func (b *Buffer) SetData(values []float32) {
gl.BindVertexArray(b.VAOID) gl.BindVertexArray(b.VAOID)
gl.BindBuffer(gl.ARRAY_BUFFER, b.BufID) gl.BindBuffer(gl.ARRAY_BUFFER, b.BufID)
gl.BufferData(gl.ARRAY_BUFFER, len(values)*4, gl.Ptr(values), BufUsage_Static.ToGL()) sizeInBytes := len(values) * 4
if sizeInBytes == 0 {
gl.BufferData(gl.ARRAY_BUFFER, 0, gl.Ptr(nil), BufUsage_Static.ToGL())
} else {
gl.BufferData(gl.ARRAY_BUFFER, sizeInBytes, gl.Ptr(&values[0]), BufUsage_Static.ToGL())
}
gl.BindVertexArray(0) gl.BindVertexArray(0)
gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindBuffer(gl.ARRAY_BUFFER, 0)
@ -42,7 +47,12 @@ func (b *Buffer) SetIndexBufData(values []uint32) {
gl.BindVertexArray(b.VAOID) gl.BindVertexArray(b.VAOID)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.IndexBufID) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.IndexBufID)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(values)*4, gl.Ptr(values), BufUsage_Static.ToGL()) sizeInBytes := len(values) * 4
if sizeInBytes == 0 {
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, 0, gl.Ptr(nil), BufUsage_Static.ToGL())
} else {
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, sizeInBytes, gl.Ptr(&values[0]), BufUsage_Static.ToGL())
}
gl.BindVertexArray(0) gl.BindVertexArray(0)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
@ -54,6 +64,8 @@ func (b *Buffer) GetLayout() []Element {
return e return e
} }
//SetLayout updates the layout object and the corresponding vertex attributes.
//Vertex attributes are also enabled.
func (b *Buffer) SetLayout(layout ...Element) { func (b *Buffer) SetLayout(layout ...Element) {
b.layout = layout b.layout = layout
@ -64,6 +76,20 @@ func (b *Buffer) SetLayout(layout ...Element) {
b.layout[i].Offset = int(b.Stride) b.layout[i].Offset = int(b.Stride)
b.Stride += b.layout[i].Size() b.Stride += b.layout[i].Size()
} }
//Set opengl stuff
b.Bind()
//NOTE: VBOs are only bound at 'VertexAttribPointer', not BindBUffer, so we need to bind the buffer and vao here
gl.BindBuffer(gl.ARRAY_BUFFER, b.BufID)
for i := 0; i < len(layout); i++ {
gl.EnableVertexAttribArray(uint32(i))
gl.VertexAttribPointer(uint32(i), layout[i].ElementType.CompCount(), layout[i].ElementType.GLType(), false, b.Stride, gl.PtrOffset(layout[i].Offset))
}
b.UnBind()
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
} }
func NewBuffer(layout ...Element) Buffer { func NewBuffer(layout ...Element) Buffer {

View File

@ -3,6 +3,7 @@ package engine
import ( import (
"runtime" "runtime"
"github.com/bloeys/nmage/asserts"
"github.com/bloeys/nmage/input" "github.com/bloeys/nmage/input"
"github.com/bloeys/nmage/renderer" "github.com/bloeys/nmage/renderer"
"github.com/bloeys/nmage/timing" "github.com/bloeys/nmage/timing"
@ -11,6 +12,10 @@ import (
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
) )
var (
isInited = false
)
type Window struct { type Window struct {
SDLWin *sdl.Window SDLWin *sdl.Window
GlCtx sdl.GLContext GlCtx sdl.GLContext
@ -96,6 +101,8 @@ func (w *Window) Destroy() error {
func Init() error { func Init() error {
isInited = true
runtime.LockOSThread() runtime.LockOSThread()
timing.Init() timing.Init()
err := initSDL() err := initSDL()
@ -139,6 +146,7 @@ func CreateOpenGLWindowCentered(title string, width, height int32, flags WindowF
func createWindow(title string, x, y, width, height int32, flags WindowFlags, rend renderer.Render) (*Window, error) { func createWindow(title string, x, y, width, height int32, flags WindowFlags, rend renderer.Render) (*Window, error) {
asserts.T(isInited, "engine.Init was not called!")
if x == -1 && y == -1 { if x == -1 && y == -1 {
x = sdl.WINDOWPOS_CENTERED x = sdl.WINDOWPOS_CENTERED
y = sdl.WINDOWPOS_CENTERED y = sdl.WINDOWPOS_CENTERED
@ -186,6 +194,8 @@ func initOpenGL() error {
} }
func SetVSync(enabled bool) { func SetVSync(enabled bool) {
asserts.T(isInited, "engine.Init was not called!")
if enabled { if enabled {
sdl.GLSetSwapInterval(1) sdl.GLSetSwapInterval(1)
} else { } else {

View File

@ -6,37 +6,32 @@ import (
"github.com/go-gl/gl/v4.1-core/gl" "github.com/go-gl/gl/v4.1-core/gl"
) )
var (
isRunning = false
)
type Game interface { type Game interface {
Init() Init()
Start()
FrameStart()
Update() Update()
Render() Render()
FrameEnd() FrameEnd()
ShouldRun() bool
GetWindow() *Window DeInit()
GetImGUI() nmageimgui.ImguiInfo
Deinit()
} }
func Run(g Game) { func Run(g Game, w *Window, ui nmageimgui.ImguiInfo) {
w := g.GetWindow()
ui := g.GetImGUI()
isRunning = true
g.Init() g.Init()
//Simulate an imgui frame during init so any imgui calls are allowed within init //Simulate an imgui frame during init so any imgui calls are allowed within init
tempWidth, tempHeight := w.SDLWin.GetSize() tempWidth, tempHeight := w.SDLWin.GetSize()
tempFBWidth, tempFBHeight := w.SDLWin.GLGetDrawableSize() tempFBWidth, tempFBHeight := w.SDLWin.GLGetDrawableSize()
ui.FrameStart(float32(tempWidth), float32(tempHeight)) ui.FrameStart(float32(tempWidth), float32(tempHeight))
g.Start()
ui.Render(float32(tempWidth), float32(tempHeight), tempFBWidth, tempFBHeight) ui.Render(float32(tempWidth), float32(tempHeight), tempFBWidth, tempFBHeight)
for g.ShouldRun() { for isRunning {
//PERF: Cache these //PERF: Cache these
width, height := w.SDLWin.GetSize() width, height := w.SDLWin.GetSize()
@ -46,8 +41,6 @@ func Run(g Game) {
w.handleInputs() w.handleInputs()
ui.FrameStart(float32(width), float32(height)) ui.FrameStart(float32(width), float32(height))
g.FrameStart()
g.Update() g.Update()
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
@ -60,5 +53,9 @@ func Run(g Game) {
timing.FrameEnded() timing.FrameEnded()
} }
g.Deinit() g.DeInit()
}
func Quit() {
isRunning = false
} }

2
go.mod
View File

@ -8,6 +8,6 @@ require github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784
require ( require (
github.com/bloeys/assimp-go v0.4.2 github.com/bloeys/assimp-go v0.4.2
github.com/bloeys/gglm v0.3.1 github.com/bloeys/gglm v0.41.10
github.com/inkyblackness/imgui-go/v4 v4.3.0 github.com/inkyblackness/imgui-go/v4 v4.3.0
) )

2
go.sum
View File

@ -2,6 +2,8 @@ github.com/bloeys/assimp-go v0.4.2 h1:ArVK74BCFcTO/rCGj2NgZG9xtbjnJdEn5npIeJx1Z0
github.com/bloeys/assimp-go v0.4.2/go.mod h1:my3yRxT7CfOztmvi+0svmwbaqw0KFrxaHxncoyaEIP0= github.com/bloeys/assimp-go v0.4.2/go.mod h1:my3yRxT7CfOztmvi+0svmwbaqw0KFrxaHxncoyaEIP0=
github.com/bloeys/gglm v0.3.1 h1:Sy9upW7SBsBfDXrSmEhid3aQ+7J7itej+upwcxOnPMQ= github.com/bloeys/gglm v0.3.1 h1:Sy9upW7SBsBfDXrSmEhid3aQ+7J7itej+upwcxOnPMQ=
github.com/bloeys/gglm v0.3.1/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk= github.com/bloeys/gglm v0.3.1/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk=
github.com/bloeys/gglm v0.41.10 h1:R9FMiI+VQVXAI+vDwCB7z9xqzy5VAR1657u8TQTDNKA=
github.com/bloeys/gglm v0.41.10/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784 h1:1Zi56D0LNfvkzM+BdoxKryvUEdyWO7LP8oRT+oSYJW0= github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784 h1:1Zi56D0LNfvkzM+BdoxKryvUEdyWO7LP8oRT+oSYJW0=

51
main.go
View File

@ -18,29 +18,21 @@ import (
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
) )
//BUGS:
// need to rebind the texutre if the texture (or any material values) change between draw calls
// DT handling for when it is zero is wrong! (Gives 1000 DT)
//TODO: Tasks: //TODO: Tasks:
// Build simple game
// Integrate physx
// Entities and components
// Camera class // Camera class
// Entities and components
//Low Priority: // Integrate physx
// Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing)
// Renderer batching // Renderer batching
// Scene graph // Scene graph
// Separate engine loop from rendering loop? or leave it to the user? // Separate engine loop from rendering loop? or leave it to the user?
// Abstract keys enum away from sdl // Abstract keys enum away from sdl
// Proper Asset loading // Proper Asset loading
// Audio
// Frustum culling // Frustum culling
// Material system editor with fields automatically extracted from the shader // Material system editor with fields automatically extracted from the shader
var ( var (
isRunning bool = true window *engine.Window
window *engine.Window
simpleMat *materials.Material simpleMat *materials.Material
cubeMesh *meshes.Mesh cubeMesh *meshes.Mesh
@ -57,13 +49,12 @@ var (
type OurGame struct { type OurGame struct {
Win *engine.Window Win *engine.Window
ImGUIInfo nmageimgui.ImguiInfo ImGUIInfo nmageimgui.ImguiInfo
Quitting bool
} }
func (g *OurGame) Init() { func (g *OurGame) Init() {
//Create materials //Create materials
simpleMat = materials.NewMaterial("Simple Mat", "./res/shaders/simple") simpleMat = materials.NewMaterial("Simple Mat", "./res/shaders/simple.glsl")
//Load meshes //Load meshes
var err error var err error
@ -73,14 +64,13 @@ func (g *OurGame) Init() {
} }
//Load textures //Load textures
tex, err := assets.LoadPNGTexture("./res/textures/Low poly planet.png") tex, err := assets.LoadTexturePNG("./res/textures/Low poly planet.png", nil)
if err != nil { if err != nil {
logging.ErrLog.Fatalln("Failed to load texture. Err: ", err) logging.ErrLog.Fatalln("Failed to load texture. Err: ", err)
} }
//Configure material //Configure material
simpleMat.DiffuseTex = tex.TexID simpleMat.DiffuseTex = tex.TexID
simpleMat.SetAttribute(cubeMesh.Buf)
//Movement, scale and rotation //Movement, scale and rotation
translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0)) translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0))
@ -102,16 +92,10 @@ func (g *OurGame) Init() {
simpleMat.SetUnifVec3("lightColor1", lightColor1) simpleMat.SetUnifVec3("lightColor1", lightColor1)
} }
func (g *OurGame) Start() {
}
func (g *OurGame) FrameStart() {
}
func (g *OurGame) Update() { func (g *OurGame) Update() {
if input.IsQuitClicked() { if input.IsQuitClicked() || input.KeyClicked(sdl.K_ESCAPE) {
g.Quitting = true engine.Quit()
} }
winWidth, winHeight := g.Win.SDLWin.GetSize() winWidth, winHeight := g.Win.SDLWin.GetSize()
@ -167,25 +151,13 @@ func (g *OurGame) Render() {
tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0)) tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0))
} }
g.GetWindow().SDLWin.SetTitle(fmt.Sprint("nMage (", timing.GetAvgFPS(), " fps)")) g.Win.SDLWin.SetTitle(fmt.Sprint("nMage (", timing.GetAvgFPS(), " fps)"))
} }
func (g *OurGame) FrameEnd() { func (g *OurGame) FrameEnd() {
} }
func (g *OurGame) ShouldRun() bool { func (g *OurGame) DeInit() {
return !g.Quitting
}
func (g *OurGame) GetWindow() *engine.Window {
return g.Win
}
func (g *OurGame) GetImGUI() nmageimgui.ImguiInfo {
return g.ImGUIInfo
}
func (g *OurGame) Deinit() {
g.Win.Destroy() g.Win.Destroy()
} }
@ -211,8 +183,7 @@ func main() {
ImGUIInfo: nmageimgui.NewImGUI(), ImGUIInfo: nmageimgui.NewImGUI(),
} }
engine.Run(game) engine.Run(game, window, game.ImGUIInfo)
return
} }
func updateViewMat() { func updateViewMat() {

View File

@ -3,7 +3,6 @@ package materials
import ( import (
"github.com/bloeys/gglm/gglm" "github.com/bloeys/gglm/gglm"
"github.com/bloeys/nmage/asserts" "github.com/bloeys/nmage/asserts"
"github.com/bloeys/nmage/buffers"
"github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/logging"
"github.com/bloeys/nmage/shaders" "github.com/bloeys/nmage/shaders"
"github.com/go-gl/gl/v4.1-core/gl" "github.com/go-gl/gl/v4.1-core/gl"
@ -61,23 +60,6 @@ func (m *Material) GetUnifLoc(uniformName string) int32 {
return loc return loc
} }
func (m *Material) SetAttribute(bufObj buffers.Buffer) {
bufObj.Bind()
//NOTE: VBOs are only bound at 'VertexAttribPointer', not BindBUffer, so we need to bind the buffer and vao here
gl.BindBuffer(gl.ARRAY_BUFFER, bufObj.BufID)
layout := bufObj.GetLayout()
for i := 0; i < len(layout); i++ {
gl.EnableVertexAttribArray(uint32(i))
gl.VertexAttribPointer(uint32(i), layout[i].ElementType.CompCount(), layout[i].ElementType.GLType(), false, bufObj.Stride, gl.PtrOffset(layout[i].Offset))
}
bufObj.UnBind()
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
}
func (m *Material) EnableAttribute(attribName string) { func (m *Material) EnableAttribute(attribName string) {
gl.EnableVertexAttribArray(uint32(m.GetAttribLoc(attribName))) gl.EnableVertexAttribArray(uint32(m.GetAttribLoc(attribName)))
} }
@ -124,24 +106,20 @@ func (m *Material) Delete() {
func NewMaterial(matName, shaderPath string) *Material { func NewMaterial(matName, shaderPath string) *Material {
shdrProg, err := shaders.NewShaderProgram() shdrProg, err := shaders.LoadAndCompileCombinedShader(shaderPath)
if err != nil { if err != nil {
logging.ErrLog.Fatalln("Failed to create new shader program. Err: ", err) logging.ErrLog.Fatalln("Failed to create new material. Err: ", err)
}
return &Material{Name: matName, ShaderProg: shdrProg, UnifLocs: make(map[string]int32), AttribLocs: make(map[string]int32)}
}
func NewMaterialSrc(matName string, shaderSrc []byte) *Material {
shdrProg, err := shaders.LoadAndCompileCombinedShaderSrc(shaderSrc)
if err != nil {
logging.ErrLog.Fatalln("Failed to create new material. Err: ", err)
} }
vertShader, err := shaders.LoadAndCompilerShader(shaderPath+".vert.glsl", shaders.VertexShaderType)
if err != nil {
logging.ErrLog.Fatalln("Failed to load and create vertex shader. Err: ", err)
}
fragShader, err := shaders.LoadAndCompilerShader(shaderPath+".frag.glsl", shaders.FragmentShaderType)
if err != nil {
logging.ErrLog.Fatalln("Failed to load and create fragment shader. Err: ", err)
}
shdrProg.AttachShader(vertShader)
shdrProg.AttachShader(fragShader)
shdrProg.Link()
return &Material{Name: matName, ShaderProg: shdrProg, UnifLocs: make(map[string]int32), AttribLocs: make(map[string]int32)} return &Material{Name: matName, ShaderProg: shdrProg, UnifLocs: make(map[string]int32), AttribLocs: make(map[string]int32)}
} }

View File

@ -1,13 +0,0 @@
#version 410
uniform sampler2D Texture;
in vec2 Frag_UV;
in vec4 Frag_Color;
out vec4 Out_Color;
void main()
{
Out_Color = vec4(Frag_Color.rgb, Frag_Color.a * texture(Texture, Frag_UV.st).r);
}

View File

@ -1,17 +0,0 @@
#version 410
uniform mat4 ProjMtx;
in vec2 Position;
in vec2 UV;
in vec4 Color;
out vec2 Frag_UV;
out vec4 Frag_Color;
void main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy, 0, 1);
}

View File

@ -1,26 +0,0 @@
#version 410
uniform float ambientStrength = 0.1;
uniform vec3 ambientLightColor = vec3(1, 1, 1);
uniform vec3 lightPos1;
uniform vec3 lightColor1;
uniform sampler2D diffTex;
in vec3 vertColor;
in vec3 vertNormal;
in vec2 vertUV0;
in vec3 fragPos;
out vec4 fragColor;
void main()
{
vec3 lightDir = normalize(lightPos1 - fragPos);
float diffStrength = max(0.0, dot(normalize(vertNormal), lightDir));
vec3 finalAmbientColor = ambientLightColor * ambientStrength;
vec4 texColor = texture(diffTex, vertUV0);
fragColor = vec4(texColor.rgb * vertColor * (finalAmbientColor + diffStrength*lightColor1) , texColor.a);
}

55
res/shaders/simple.glsl Executable file
View File

@ -0,0 +1,55 @@
//shader:vertex
#version 410
layout(location=0) in vec3 vertPosIn;
layout(location=1) in vec3 vertNormalIn;
layout(location=2) in vec2 vertUV0In;
layout(location=3) in vec3 vertColorIn;
out vec3 vertNormal;
out vec2 vertUV0;
out vec3 vertColor;
out vec3 fragPos;
//MVP = Model View Projection
uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projMat;
void main()
{
vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn;
vertUV0 = vertUV0In;
vertColor = vertColorIn;
fragPos = vec3(modelMat * vec4(vertPosIn, 1.0));
gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0);
}
//shader:fragment
#version 410
uniform float ambientStrength = 0.1;
uniform vec3 ambientLightColor = vec3(1, 1, 1);
uniform vec3 lightPos1;
uniform vec3 lightColor1;
uniform sampler2D diffTex;
in vec3 vertColor;
in vec3 vertNormal;
in vec2 vertUV0;
in vec3 fragPos;
out vec4 fragColor;
void main()
{
vec3 lightDir = normalize(lightPos1 - fragPos);
float diffStrength = max(0.0, dot(normalize(vertNormal), lightDir));
vec3 finalAmbientColor = ambientLightColor * ambientStrength;
vec4 texColor = texture(diffTex, vertUV0);
fragColor = vec4(texColor.rgb * vertColor * (finalAmbientColor + diffStrength*lightColor1) , texColor.a);
}

View File

@ -1,26 +0,0 @@
#version 410
layout(location=0) in vec3 vertPosIn;
layout(location=1) in vec3 vertNormalIn;
layout(location=2) in vec2 vertUV0In;
layout(location=3) in vec3 vertColorIn;
out vec3 vertNormal;
out vec2 vertUV0;
out vec3 vertColor;
out vec3 fragPos;
//MVP = Model View Projection
uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projMat;
void main()
{
vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn;
vertUV0 = vertUV0In;
vertColor = vertColorIn;
fragPos = vec3(modelMat * vec4(vertPosIn, 1.0));
gl_Position = projMat * viewMat * modelMat * vec4(vertPosIn, 1.0);
}

View File

@ -1,6 +1,7 @@
package shaders package shaders
import ( import (
"bytes"
"errors" "errors"
"os" "os"
"strings" "strings"
@ -28,14 +29,69 @@ func NewShaderProgram() (ShaderProgram, error) {
return ShaderProgram{ID: id}, nil return ShaderProgram{ID: id}, nil
} }
func LoadAndCompilerShader(shaderPath string, shaderType ShaderType) (Shader, error) { func LoadAndCompileCombinedShader(shaderPath string) (ShaderProgram, error) {
shaderSource, err := os.ReadFile(shaderPath) combinedSource, err := os.ReadFile(shaderPath)
if err != nil { if err != nil {
logging.ErrLog.Println("Failed to read shader. Err: ", err) logging.ErrLog.Println("Failed to read shader. Err: ", err)
return Shader{}, err return ShaderProgram{}, err
} }
return LoadAndCompileCombinedShaderSrc(combinedSource)
}
func LoadAndCompileCombinedShaderSrc(shaderSrc []byte) (ShaderProgram, error) {
shaderSources := bytes.Split(shaderSrc, []byte("//shader:"))
if len(shaderSources) == 1 {
return ShaderProgram{}, errors.New("failed to read combined shader. Did not find '//shader:vertex' or '//shader:fragment'")
}
shdrProg, err := NewShaderProgram()
if err != nil {
return ShaderProgram{}, errors.New("failed to create new shader program. Err: " + err.Error())
}
loadedShdrCount := 0
for i := 0; i < len(shaderSources); i++ {
src := shaderSources[i]
//This can happen when the shader type is at the start of the file
if len(bytes.TrimSpace(src)) == 0 {
continue
}
var shdrType ShaderType
if bytes.HasPrefix(src, []byte("vertex")) {
src = src[6:]
shdrType = VertexShaderType
} else if bytes.HasPrefix(src, []byte("fragment")) {
src = src[8:]
shdrType = FragmentShaderType
} else {
return ShaderProgram{}, errors.New("unknown shader type. Must be '//shader:vertex' or '//shader:fragment'")
}
shdr, err := CompileShaderOfType(src, shdrType)
if err != nil {
return ShaderProgram{}, err
}
loadedShdrCount++
shdrProg.AttachShader(shdr)
}
if loadedShdrCount == 0 {
return ShaderProgram{}, errors.New("no valid shaders found. Please put '//shader:vertex' or '//shader:fragment' before your shaders")
}
shdrProg.Link()
return shdrProg, nil
}
func CompileShaderOfType(shaderSource []byte, shaderType ShaderType) (Shader, error) {
shaderID := gl.CreateShader(uint32(shaderType)) shaderID := gl.CreateShader(uint32(shaderType))
if shaderID == 0 { if shaderID == 0 {
logging.ErrLog.Println("Failed to create shader.") logging.ErrLog.Println("Failed to create shader.")

View File

@ -43,7 +43,7 @@ func FrameEnded() {
//Calculate new dt //Calculate new dt
dt = float32(time.Since(frameStart).Seconds()) dt = float32(time.Since(frameStart).Seconds())
if dt == 0 { if dt == 0 {
dt = float32(time.Microsecond) dt = float32(time.Microsecond.Seconds())
} }
} }

View File

@ -66,8 +66,7 @@ func (i *ImguiInfo) Render(winWidth, winHeight float32, fbWidth, fbHeight int32)
// DisplayMin is typically (0,0) for single viewport apps. // DisplayMin is typically (0,0) for single viewport apps.
i.Mat.Bind() i.Mat.Bind()
i.Mat.SetUnifInt32("Texture", 0)
gl.Uniform1i(gl.GetUniformLocation(i.Mat.ShaderProg.ID, gl.Str("Texture\x00")), 0)
//PERF: only update the ortho matrix on window resize //PERF: only update the ortho matrix on window resize
orthoMat := gglm.Ortho(0, float32(winWidth), 0, float32(winHeight), 0, 20) orthoMat := gglm.Ortho(0, float32(winWidth), 0, float32(winHeight), 0, 20)
@ -149,11 +148,47 @@ func (i *ImguiInfo) AddFontTTF(fontPath string, fontSize float32, fontConfig *im
return f return f
} }
const imguiShdrSrc = `
//shader:vertex
#version 410
uniform mat4 ProjMtx;
in vec2 Position;
in vec2 UV;
in vec4 Color;
out vec2 Frag_UV;
out vec4 Frag_Color;
void main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy, 0, 1);
}
//shader:fragment
#version 410
uniform sampler2D Texture;
in vec2 Frag_UV;
in vec4 Frag_Color;
out vec4 Out_Color;
void main()
{
Out_Color = vec4(Frag_Color.rgb, Frag_Color.a * texture(Texture, Frag_UV.st).r);
}
`
func NewImGUI() ImguiInfo { func NewImGUI() ImguiInfo {
imguiInfo := ImguiInfo{ imguiInfo := ImguiInfo{
ImCtx: imgui.CreateContext(nil), ImCtx: imgui.CreateContext(nil),
Mat: materials.NewMaterial("ImGUI Mat", "./res/shaders/imgui"), Mat: materials.NewMaterialSrc("ImGUI Mat", []byte(imguiShdrSrc)),
} }
imIO := imgui.CurrentIO() imIO := imgui.CurrentIO()