From 709dc062cc73ab908b28d9759766503e771120ef Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 26 Feb 2022 22:07:59 +0400 Subject: [PATCH] Day 15: Basic renderer+improve material system+lockosthread on init --- engine/engine.go | 21 +++++++++---- engine/game.go | 1 + main.go | 59 +++++++++++------------------------ materials/material.go | 23 ++++++-------- meshes/mesh.go | 10 ++---- renderer/rend3dgl/rend3dgl.go | 41 ++++++++++++++++++++++++ renderer/renderer.go | 12 +++++++ timing/timing.go | 26 +++++++++++++++ 8 files changed, 124 insertions(+), 69 deletions(-) create mode 100755 renderer/rend3dgl/rend3dgl.go create mode 100755 renderer/renderer.go diff --git a/engine/engine.go b/engine/engine.go index 4dff4e8..1226dad 100755 --- a/engine/engine.go +++ b/engine/engine.go @@ -1,7 +1,10 @@ package engine import ( + "runtime" + "github.com/bloeys/nmage/input" + "github.com/bloeys/nmage/renderer" "github.com/bloeys/nmage/timing" "github.com/go-gl/gl/v4.1-core/gl" "github.com/inkyblackness/imgui-go/v4" @@ -12,6 +15,7 @@ type Window struct { SDLWin *sdl.Window GlCtx sdl.GLContext EventCallbacks []func(sdl.Event) + Rend renderer.Render } func (w *Window) handleInputs() { @@ -92,6 +96,7 @@ func (w *Window) Destroy() error { func Init() error { + runtime.LockOSThread() timing.Init() err := initSDL() @@ -124,15 +129,15 @@ func initSDL() error { return nil } -func CreateOpenGLWindow(title string, x, y, width, height int32, flags WindowFlags) (*Window, error) { - return createWindow(title, x, y, width, height, WindowFlags_OPENGL|flags) +func CreateOpenGLWindow(title string, x, y, width, height int32, flags WindowFlags, rend renderer.Render) (*Window, error) { + return createWindow(title, x, y, width, height, WindowFlags_OPENGL|flags, rend) } -func CreateOpenGLWindowCentered(title string, width, height int32, flags WindowFlags) (*Window, error) { - return createWindow(title, -1, -1, width, height, WindowFlags_OPENGL|flags) +func CreateOpenGLWindowCentered(title string, width, height int32, flags WindowFlags, rend renderer.Render) (*Window, error) { + return createWindow(title, -1, -1, width, height, WindowFlags_OPENGL|flags, rend) } -func createWindow(title string, x, y, width, height int32, flags WindowFlags) (*Window, error) { +func createWindow(title string, x, y, width, height int32, flags WindowFlags, rend renderer.Render) (*Window, error) { if x == -1 && y == -1 { x = sdl.WINDOWPOS_CENTERED @@ -143,7 +148,11 @@ func createWindow(title string, x, y, width, height int32, flags WindowFlags) (* if err != nil { return nil, err } - win := &Window{SDLWin: sdlWin, EventCallbacks: make([]func(sdl.Event), 0)} + win := &Window{ + SDLWin: sdlWin, + EventCallbacks: make([]func(sdl.Event), 0), + Rend: rend, + } win.GlCtx, err = sdlWin.GLCreateContext() if err != nil { diff --git a/engine/game.go b/engine/game.go index 1c246a6..1b46247 100755 --- a/engine/game.go +++ b/engine/game.go @@ -46,6 +46,7 @@ func Run(g Game) { w.SDLWin.GLSwap() g.FrameEnd() + w.Rend.FrameEnd() timing.FrameEnded() } diff --git a/main.go b/main.go index ea6960f..3886038 100755 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "runtime" "github.com/bloeys/assimp-go/asig" "github.com/bloeys/gglm/gglm" @@ -12,24 +11,28 @@ import ( "github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/meshes" + "github.com/bloeys/nmage/renderer/rend3dgl" "github.com/bloeys/nmage/timing" nmageimgui "github.com/bloeys/nmage/ui/imgui" - "github.com/go-gl/gl/v4.1-core/gl" "github.com/inkyblackness/imgui-go/v4" "github.com/veandco/go-sdl2/sdl" ) //TODO: Tasks: -//Proper rendering setup -//Entities and components -//Camera class -//Audio -//Flesh out the material system +// Build simple game +// Integrate physx +// Entities and components +// Camera class //Low Priority: +// Renderer batching +// Scene graph +// Separate engine loop from rendering loop? or leave it to the user? // Abstract keys enum away from sdl -// Abstract UI // Proper Asset loading +// Audio +// Frustum culling +// Material system editor with fields automatically extracted from the shader var ( isRunning bool = true @@ -70,14 +73,9 @@ func (g *OurGame) Init() { if err != nil { logging.ErrLog.Fatalln("Failed to load texture. Err: ", err) } - cubeMesh.AddTexture(tex) - //Set mesh textures on material - for _, v := range cubeMesh.TextureIDs { - simpleMat.AddTextureID(v) - } - - //Enable vertex attributes + //Configure material + simpleMat.DiffuseTex = tex.TexID simpleMat.SetAttribute(cubeMesh.Buf) //Movement, scale and rotation @@ -149,42 +147,23 @@ func (g *OurGame) Update() { imgui.DragFloat3("Cam Pos", &camPos.Data) } -var dtAccum float32 = 0 -var lastElapsedTime uint64 = 0 -var framesSinceLastFPSUpdate uint = 0 - func (g *OurGame) Render() { - simpleMat.Bind() - cubeMesh.Buf.Bind() tempModelMat := modelMat.Clone() rowSize := 100 for y := 0; y < rowSize; y++ { for x := 0; x < rowSize; x++ { - simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4) - gl.DrawElements(gl.TRIANGLES, cubeMesh.Buf.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0)) + tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)) + window.Rend.Draw(cubeMesh, tempModelMat, simpleMat) } - simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0)).Mat4) - } - simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4) - - dtAccum += timing.DT() - framesSinceLastFPSUpdate++ - if timing.ElapsedTime() > lastElapsedTime { - - avgDT := dtAccum / float32(framesSinceLastFPSUpdate) - g.GetWindow().SDLWin.SetTitle(fmt.Sprint("nMage (", 1/avgDT, " fps)")) - - dtAccum = 0 - framesSinceLastFPSUpdate = 0 + tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0)) } - lastElapsedTime = timing.ElapsedTime() + g.GetWindow().SDLWin.SetTitle(fmt.Sprint("nMage (", timing.GetAvgFPS(), " fps)")) } func (g *OurGame) FrameEnd() { - } func (g *OurGame) ShouldRun() bool { @@ -205,8 +184,6 @@ func (g *OurGame) Deinit() { func main() { - runtime.LockOSThread() - //Init engine err := engine.Init() if err != nil { @@ -214,7 +191,7 @@ func main() { } //Create window - window, err = engine.CreateOpenGLWindowCentered("nMage", 1280, 720, engine.WindowFlags_RESIZABLE) + window, err = engine.CreateOpenGLWindowCentered("nMage", 1280, 720, engine.WindowFlags_RESIZABLE, rend3dgl.NewRend3DGL()) if err != nil { logging.ErrLog.Fatalln("Failed to create window. Err: ", err) } diff --git a/materials/material.go b/materials/material.go index b002909..ae3cac9 100755 --- a/materials/material.go +++ b/materials/material.go @@ -12,7 +12,8 @@ import ( type Material struct { Name string ShaderProg shaders.ShaderProgram - TexIDs []uint32 + + DiffuseTex uint32 UnifLocs map[string]int32 AttribLocs map[string]int32 @@ -21,19 +22,17 @@ type Material struct { func (m *Material) Bind() { gl.UseProgram(m.ShaderProg.ID) - for i, v := range m.TexIDs { - gl.ActiveTexture(gl.TEXTURE0 + uint32(i)) - gl.BindTexture(gl.TEXTURE_2D, v) - } + + gl.ActiveTexture(gl.TEXTURE0) + gl.BindTexture(gl.TEXTURE_2D, m.DiffuseTex) } func (m *Material) UnBind() { - gl.UseProgram(0) - for i := range m.TexIDs { - gl.ActiveTexture(gl.TEXTURE0 + uint32(i)) - gl.BindTexture(gl.TEXTURE_2D, 0) - } + + //TODO: Should we unbind textures here? Are these two lines needed? + // gl.ActiveTexture(gl.TEXTURE0) + // gl.BindTexture(gl.TEXTURE_2D, 0) } func (m *Material) GetAttribLoc(attribName string) int32 { @@ -79,10 +78,6 @@ func (m *Material) SetAttribute(bufObj buffers.Buffer) { gl.BindBuffer(gl.ARRAY_BUFFER, 0) } -func (m *Material) AddTextureID(texID uint32) { - m.TexIDs = append(m.TexIDs, texID) -} - func (m *Material) EnableAttribute(attribName string) { gl.EnableVertexAttribArray(uint32(m.GetAttribLoc(attribName))) } diff --git a/meshes/mesh.go b/meshes/mesh.go index c339570..8cbb9d9 100755 --- a/meshes/mesh.go +++ b/meshes/mesh.go @@ -7,18 +7,12 @@ import ( "github.com/bloeys/assimp-go/asig" "github.com/bloeys/gglm/gglm" "github.com/bloeys/nmage/asserts" - "github.com/bloeys/nmage/assets" "github.com/bloeys/nmage/buffers" ) type Mesh struct { - Name string - TextureIDs []uint32 - Buf buffers.Buffer -} - -func (m *Mesh) AddTexture(tex assets.Texture) { - m.TextureIDs = append(m.TextureIDs, tex.TexID) + Name string + Buf buffers.Buffer } func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, error) { diff --git a/renderer/rend3dgl/rend3dgl.go b/renderer/rend3dgl/rend3dgl.go new file mode 100755 index 0000000..e68c766 --- /dev/null +++ b/renderer/rend3dgl/rend3dgl.go @@ -0,0 +1,41 @@ +package rend3dgl + +import ( + "github.com/bloeys/gglm/gglm" + "github.com/bloeys/nmage/materials" + "github.com/bloeys/nmage/meshes" + "github.com/bloeys/nmage/renderer" + "github.com/go-gl/gl/v4.1-core/gl" +) + +var _ renderer.Render = &Rend3DGL{} + +type Rend3DGL struct { + BoundMesh *meshes.Mesh + BoundMat *materials.Material +} + +func (r3d *Rend3DGL) Draw(mesh *meshes.Mesh, trMat *gglm.TrMat, mat *materials.Material) { + + if mesh != r3d.BoundMesh { + mesh.Buf.Bind() + r3d.BoundMesh = mesh + } + + if mat != r3d.BoundMat { + mat.Bind() + r3d.BoundMat = mat + } + + mat.SetUnifMat4("modelMat", &trMat.Mat4) + gl.DrawElements(gl.TRIANGLES, mesh.Buf.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0)) +} + +func (r3d *Rend3DGL) FrameEnd() { + r3d.BoundMesh = nil + r3d.BoundMat = nil +} + +func NewRend3DGL() *Rend3DGL { + return &Rend3DGL{} +} diff --git a/renderer/renderer.go b/renderer/renderer.go new file mode 100755 index 0000000..333a348 --- /dev/null +++ b/renderer/renderer.go @@ -0,0 +1,12 @@ +package renderer + +import ( + "github.com/bloeys/gglm/gglm" + "github.com/bloeys/nmage/materials" + "github.com/bloeys/nmage/meshes" +) + +type Render interface { + Draw(mesh *meshes.Mesh, trMat *gglm.TrMat, mat *materials.Material) + FrameEnd() +} diff --git a/timing/timing.go b/timing/timing.go index 7aabb48..e19d5ed 100755 --- a/timing/timing.go +++ b/timing/timing.go @@ -8,6 +8,12 @@ var ( dt float32 = 0.01 frameStart time.Time startTime time.Time + + //fps calculator vars + dtAccum float32 = 1 + lastElapsedTime uint64 = 0 + framesSinceLastFPSUpdate uint = 0 + avgFps float32 = 1 ) func Init() { @@ -15,11 +21,26 @@ func Init() { } func FrameStarted() { + frameStart = time.Now() + + //fps stuff + dtAccum += dt + framesSinceLastFPSUpdate++ + et := ElapsedTime() + if et > lastElapsedTime { + avgDT := dtAccum / float32(framesSinceLastFPSUpdate) + avgFps = 1 / avgDT + + dtAccum = 0 + framesSinceLastFPSUpdate = 0 + } + lastElapsedTime = et } func FrameEnded() { + //Calculate new dt dt = float32(time.Since(frameStart).Seconds()) if dt == 0 { dt = float32(time.Microsecond) @@ -31,6 +52,11 @@ func DT() float32 { return dt } +//GetAvgFPS returns the fps averaged over 1 second +func GetAvgFPS() float32 { + return avgFps +} + //ElapsedTime is time since game start func ElapsedTime() uint64 { return uint64(time.Since(startTime).Seconds())