From 52b77e017e26ae5b65504a576950aabcfa98c836 Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 23 Jul 2022 22:51:57 +0400 Subject: [PATCH] Camera package+ rename asserts->assert --- asserts/asserts.go => assert/assert.go | 2 +- buffers/buf_usage.go | 4 +- buffers/element.go | 10 +- camera/camera.go | 96 +++++++++++++++++++ engine/engine.go | 6 +- main.go | 123 +++++++++++++++---------- materials/material.go | 6 +- meshes/mesh.go | 18 ++-- ui/imgui/imgui.go | 6 +- 9 files changed, 195 insertions(+), 76 deletions(-) rename asserts/asserts.go => assert/assert.go (92%) create mode 100755 camera/camera.go diff --git a/asserts/asserts.go b/assert/assert.go similarity index 92% rename from asserts/asserts.go rename to assert/assert.go index dc47efb..8505eaf 100755 --- a/asserts/asserts.go +++ b/assert/assert.go @@ -1,4 +1,4 @@ -package asserts +package assert import ( "github.com/bloeys/nmage/consts" diff --git a/buffers/buf_usage.go b/buffers/buf_usage.go index c6a6bfe..c541804 100755 --- a/buffers/buf_usage.go +++ b/buffers/buf_usage.go @@ -3,7 +3,7 @@ package buffers import ( "fmt" - "github.com/bloeys/nmage/asserts" + "github.com/bloeys/nmage/assert" "github.com/go-gl/gl/v4.1-core/gl" ) @@ -28,6 +28,6 @@ func (b BufUsage) ToGL() uint32 { return gl.STREAM_DRAW } - asserts.T(false, fmt.Sprintf("Unexpected BufUsage value '%v'", b)) + assert.T(false, fmt.Sprintf("Unexpected BufUsage value '%v'", b)) return 0 } diff --git a/buffers/element.go b/buffers/element.go index 1807e0e..4c57936 100755 --- a/buffers/element.go +++ b/buffers/element.go @@ -3,7 +3,7 @@ package buffers import ( "fmt" - "github.com/bloeys/nmage/asserts" + "github.com/bloeys/nmage/assert" "github.com/go-gl/gl/v4.1-core/gl" ) @@ -45,7 +45,7 @@ func (dt ElementType) GLType() uint32 { return gl.FLOAT default: - asserts.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) + assert.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) return 0 } } @@ -68,7 +68,7 @@ func (dt ElementType) CompSize() int32 { return 4 default: - asserts.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) + assert.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) return 0 } } @@ -92,7 +92,7 @@ func (dt ElementType) CompCount() int32 { return 4 default: - asserts.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) + assert.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) return 0 } } @@ -116,7 +116,7 @@ func (dt ElementType) Size() int32 { return 4 * 4 default: - asserts.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) + assert.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) return 0 } } diff --git a/camera/camera.go b/camera/camera.go new file mode 100755 index 0000000..c6d1c3e --- /dev/null +++ b/camera/camera.go @@ -0,0 +1,96 @@ +package camera + +import ( + "github.com/bloeys/gglm/gglm" +) + +type Type int32 + +const ( + Type_Unknown Type = iota + Type_Perspective + Type_Orthographic +) + +type Camera struct { + Type Type + + Pos gglm.Vec3 + Target gglm.Vec3 + // Forward gglm.Vec3 + WorldUp gglm.Vec3 + + NearClip float32 + FarClip float32 + + // Perspective data + Fov float32 + AspectRatio float32 + + // Ortho data + Left, Right, Top, Bottom float32 + + // Matrices + ViewMat gglm.Mat4 + ProjMat gglm.Mat4 +} + +// Update recalculates view and projection matrices +func (c *Camera) Update() { + + c.ViewMat = gglm.LookAt(&c.Pos, &c.Target, &c.WorldUp).Mat4 + + if c.Type == Type_Perspective { + c.ProjMat = *gglm.Perspective(c.Fov, c.AspectRatio, c.NearClip, c.FarClip) + } else { + c.ProjMat = gglm.Ortho(c.Left, c.Right, c.Top, c.Bottom, c.NearClip, c.FarClip).Mat4 + } +} + +func (c *Camera) LookAt(targetPos, worldUp *gglm.Vec3) { + c.Target = *targetPos + c.WorldUp = *worldUp + c.Update() +} + +func NewPerspective(pos, targetPos, worldUp *gglm.Vec3, nearClip, farClip, fovRadians, aspectRatio float32) *Camera { + + cam := &Camera{ + Type: Type_Perspective, + Pos: *pos, + // Forward: *gglm.NewVec3(0, 0, 1), + Target: *targetPos, + WorldUp: *worldUp, + + NearClip: nearClip, + FarClip: farClip, + + Fov: fovRadians, + AspectRatio: aspectRatio, + } + cam.Update() + + return cam +} + +func NewOrthographic(pos, targetPos, worldUp *gglm.Vec3, nearClip, farClip, left, right, top, bottom float32) *Camera { + + cam := &Camera{ + Type: Type_Orthographic, + Pos: *pos, + // Forward: *gglm.NewVec3(0, 0, 0), + Target: *targetPos, + WorldUp: *worldUp, + + NearClip: nearClip, + FarClip: farClip, + + Left: left, + Right: right, + Top: top, + Bottom: bottom, + } + cam.Update() + + return cam +} diff --git a/engine/engine.go b/engine/engine.go index 988fcdc..5f4f4cb 100755 --- a/engine/engine.go +++ b/engine/engine.go @@ -3,7 +3,7 @@ package engine import ( "runtime" - "github.com/bloeys/nmage/asserts" + "github.com/bloeys/nmage/assert" "github.com/bloeys/nmage/input" "github.com/bloeys/nmage/renderer" "github.com/bloeys/nmage/timing" @@ -146,7 +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) { - asserts.T(isInited, "engine.Init was not called!") + assert.T(isInited, "engine.Init was not called!") if x == -1 && y == -1 { x = sdl.WINDOWPOS_CENTERED y = sdl.WINDOWPOS_CENTERED @@ -194,7 +194,7 @@ func initOpenGL() error { } func SetVSync(enabled bool) { - asserts.T(isInited, "engine.Init was not called!") + assert.T(isInited, "engine.Init was not called!") if enabled { sdl.GLSetSwapInterval(1) diff --git a/main.go b/main.go index aa5bfdf..ae0338f 100755 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "github.com/bloeys/assimp-go/asig" "github.com/bloeys/gglm/gglm" "github.com/bloeys/nmage/assets" + "github.com/bloeys/nmage/camera" "github.com/bloeys/nmage/engine" "github.com/bloeys/nmage/input" "github.com/bloeys/nmage/logging" @@ -18,8 +19,7 @@ import ( "github.com/veandco/go-sdl2/sdl" ) -//TODO: Tasks: -// Camera class +// @Todo: // Entities and components // Integrate physx // Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing) @@ -34,13 +34,12 @@ import ( var ( window *engine.Window + cam *camera.Camera + simpleMat *materials.Material cubeMesh *meshes.Mesh - modelMat = gglm.NewTrMatId() - projMat = &gglm.Mat4{} - camPos = gglm.NewVec3(0, 0, -10) - camForward = gglm.NewVec3(0, 0, 1) + modelMat = gglm.NewTrMatId() lightPos1 = gglm.NewVec3(2, 2, 0) lightColor1 = gglm.NewVec3(1, 1, 1) @@ -51,6 +50,48 @@ type OurGame struct { ImGUIInfo nmageimgui.ImguiInfo } +func main() { + + //Init engine + err := engine.Init() + if err != nil { + logging.ErrLog.Fatalln("Failed to init nMage. Err:", err) + } + + //Create window + window, err = engine.CreateOpenGLWindowCentered("nMage", 1280, 720, engine.WindowFlags_RESIZABLE, rend3dgl.NewRend3DGL()) + if err != nil { + logging.ErrLog.Fatalln("Failed to create window. Err: ", err) + } + defer window.Destroy() + + engine.SetVSync(false) + + game := &OurGame{ + Win: window, + ImGUIInfo: nmageimgui.NewImGUI(), + } + window.EventCallbacks = append(window.EventCallbacks, game.handleWindowEvents) + + engine.Run(game, window, game.ImGUIInfo) +} + +func (g *OurGame) handleWindowEvents(e sdl.Event) { + + switch e := e.(type) { + case *sdl.WindowEvent: + if e.Event == sdl.WINDOWEVENT_SIZE_CHANGED { + + width := e.Data1 + height := e.Data2 + cam.AspectRatio = float32(width) / float32(height) + cam.Update() + + simpleMat.SetUnifMat4("projMat", &cam.ProjMat) + } + } +} + func (g *OurGame) Init() { //Create materials @@ -80,16 +121,24 @@ func (g *OurGame) Init() { modelMat.Mul(translationMat.Mul(rotMat.Mul(scaleMat))) simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4) - //Moves objects into the cameras view - updateViewMat() + // Camera + winWidth, winHeight := g.Win.SDLWin.GetSize() + cam = camera.NewPerspective( + gglm.NewVec3(0, 0, -10), + gglm.NewVec3(0, 0, -9), + gglm.NewVec3(0, 1, 0), + 0.1, 20, + 45*gglm.Deg2Rad, + float32(winWidth)/float32(winHeight), + ) + simpleMat.SetUnifMat4("projMat", &cam.ProjMat) - //Perspective/Depth - projMat := gglm.Perspective(45*gglm.Deg2Rad, float32(1280)/float32(720), 0.1, 500) - simpleMat.SetUnifMat4("projMat", projMat) + updateViewMat() //Lights simpleMat.SetUnifVec3("lightPos1", lightPos1) simpleMat.SetUnifVec3("lightColor1", lightColor1) + } func (g *OurGame) Update() { @@ -98,34 +147,30 @@ func (g *OurGame) Update() { engine.Quit() } - winWidth, winHeight := g.Win.SDLWin.GetSize() - projMat = gglm.Perspective(45*gglm.Deg2Rad, float32(winWidth)/float32(winHeight), 0.1, 20) - simpleMat.SetUnifMat4("projMat", projMat) - //Camera movement var camSpeed float32 = 15 if input.KeyDown(sdl.K_w) { - camPos.Data[1] += camSpeed * timing.DT() + cam.Pos.AddY(camSpeed * timing.DT()) updateViewMat() } if input.KeyDown(sdl.K_s) { - camPos.Data[1] -= camSpeed * timing.DT() + cam.Pos.AddY(-camSpeed * timing.DT()) updateViewMat() } if input.KeyDown(sdl.K_d) { - camPos.Data[0] += camSpeed * timing.DT() + cam.Pos.AddX(camSpeed * timing.DT()) updateViewMat() } if input.KeyDown(sdl.K_a) { - camPos.Data[0] -= camSpeed * timing.DT() + cam.Pos.AddX(-camSpeed * timing.DT()) updateViewMat() } if input.GetMouseWheelYNorm() > 0 { - camPos.Data[2] += 1 + cam.Pos.AddZ(1) updateViewMat() } else if input.GetMouseWheelYNorm() < 0 { - camPos.Data[2] -= 1 + cam.Pos.AddZ(-1) updateViewMat() } @@ -135,7 +180,7 @@ func (g *OurGame) Update() { simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4) } - imgui.DragFloat3("Cam Pos", &camPos.Data) + imgui.DragFloat3("Cam Pos", &cam.Pos.Data) } func (g *OurGame) Render() { @@ -161,33 +206,11 @@ func (g *OurGame) DeInit() { g.Win.Destroy() } -func main() { - - //Init engine - err := engine.Init() - if err != nil { - logging.ErrLog.Fatalln("Failed to init nMage. Err:", err) - } - - //Create window - window, err = engine.CreateOpenGLWindowCentered("nMage", 1280, 720, engine.WindowFlags_RESIZABLE, rend3dgl.NewRend3DGL()) - if err != nil { - logging.ErrLog.Fatalln("Failed to create window. Err: ", err) - } - defer window.Destroy() - - engine.SetVSync(false) - - game := &OurGame{ - Win: window, - ImGUIInfo: nmageimgui.NewImGUI(), - } - - engine.Run(game, window, game.ImGUIInfo) -} - func updateViewMat() { - targetPos := camPos.Clone().Add(camForward) - viewMat := gglm.LookAt(camPos, targetPos, gglm.NewVec3(0, 1, 0)) - simpleMat.SetUnifMat4("viewMat", &viewMat.Mat4) + target := cam.Pos.Clone() + target.AddZ(1) + cam.Target = *target + cam.Update() + + simpleMat.SetUnifMat4("viewMat", &cam.ViewMat) } diff --git a/materials/material.go b/materials/material.go index c7afa96..9583190 100755 --- a/materials/material.go +++ b/materials/material.go @@ -2,7 +2,7 @@ package materials import ( "github.com/bloeys/gglm/gglm" - "github.com/bloeys/nmage/asserts" + "github.com/bloeys/nmage/assert" "github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/shaders" "github.com/go-gl/gl/v4.1-core/gl" @@ -42,7 +42,7 @@ func (m *Material) GetAttribLoc(attribName string) int32 { } loc = gl.GetAttribLocation(m.ShaderProg.ID, gl.Str(attribName+"\x00")) - asserts.T(loc != -1, "Attribute '"+attribName+"' doesn't exist on material "+m.Name) + assert.T(loc != -1, "Attribute '"+attribName+"' doesn't exist on material "+m.Name) m.AttribLocs[attribName] = loc return loc } @@ -55,7 +55,7 @@ func (m *Material) GetUnifLoc(uniformName string) int32 { } loc = gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00")) - asserts.T(loc != -1, "Uniform '"+uniformName+"' doesn't exist on material "+m.Name) + assert.T(loc != -1, "Uniform '"+uniformName+"' doesn't exist on material "+m.Name) m.UnifLocs[uniformName] = loc return loc } diff --git a/meshes/mesh.go b/meshes/mesh.go index 8cbb9d9..340f9d0 100755 --- a/meshes/mesh.go +++ b/meshes/mesh.go @@ -6,7 +6,7 @@ import ( "github.com/bloeys/assimp-go/asig" "github.com/bloeys/gglm/gglm" - "github.com/bloeys/nmage/asserts" + "github.com/bloeys/nmage/assert" "github.com/bloeys/nmage/buffers" ) @@ -31,7 +31,7 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, sceneMesh := scene.Meshes[0] mesh.Buf = buffers.NewBuffer() - asserts.T(len(sceneMesh.TexCoords[0]) > 0, "Mesh has no UV0") + assert.T(len(sceneMesh.TexCoords[0]) > 0, "Mesh has no UV0") layoutToUse := []buffers.Element{{ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec2}} if len(sceneMesh.ColorSets) > 0 && len(sceneMesh.ColorSets[0]) > 0 { @@ -73,9 +73,9 @@ type arrToInterleave struct { func (a *arrToInterleave) get(i int) []float32 { - asserts.T(len(a.V2s) == 0 || len(a.V3s) == 0, "One array should be set in arrToInterleave, but both arrays are set") - asserts.T(len(a.V2s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set") - asserts.T(len(a.V3s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set") + assert.T(len(a.V2s) == 0 || len(a.V3s) == 0, "One array should be set in arrToInterleave, but both arrays are set") + assert.T(len(a.V2s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set") + assert.T(len(a.V3s) == 0 || len(a.V4s) == 0, "One array should be set in arrToInterleave, but both arrays are set") if len(a.V2s) > 0 { return a.V2s[i].Data[:] @@ -88,8 +88,8 @@ func (a *arrToInterleave) get(i int) []float32 { func interleave(arrs ...arrToInterleave) []float32 { - asserts.T(len(arrs) > 0, "No input sent to interleave") - asserts.T(len(arrs[0].V2s) > 0 || len(arrs[0].V3s) > 0 || len(arrs[0].V4s) > 0, "Interleave arrays are empty") + assert.T(len(arrs) > 0, "No input sent to interleave") + assert.T(len(arrs[0].V2s) > 0 || len(arrs[0].V3s) > 0 || len(arrs[0].V4s) > 0, "Interleave arrays are empty") elementCount := 0 if len(arrs[0].V2s) > 0 { @@ -104,7 +104,7 @@ func interleave(arrs ...arrToInterleave) []float32 { totalSize := 0 for i := 0; i < len(arrs); i++ { - asserts.T(len(arrs[i].V2s) == elementCount || len(arrs[i].V3s) == elementCount || len(arrs[i].V4s) == elementCount, "Mesh vertex data given to interleave is not the same length") + assert.T(len(arrs[i].V2s) == elementCount || len(arrs[i].V3s) == elementCount || len(arrs[i].V4s) == elementCount, "Mesh vertex data given to interleave is not the same length") if len(arrs[i].V2s) > 0 { totalSize += len(arrs[i].V2s) * 2 @@ -152,7 +152,7 @@ func flattenVec4(vec4s []gglm.Vec4) []float32 { func flattenFaces(faces []asig.Face) []uint32 { - asserts.T(len(faces[0].Indices) == 3, fmt.Sprintf("Face doesn't have 3 indices. Index count: %v\n", len(faces[0].Indices))) + assert.T(len(faces[0].Indices) == 3, fmt.Sprintf("Face doesn't have 3 indices. Index count: %v\n", len(faces[0].Indices))) uints := make([]uint32, len(faces)*3) for i := 0; i < len(faces); i++ { diff --git a/ui/imgui/imgui.go b/ui/imgui/imgui.go index a2d9e23..9e5146e 100755 --- a/ui/imgui/imgui.go +++ b/ui/imgui/imgui.go @@ -2,7 +2,7 @@ package nmageimgui import ( "github.com/bloeys/gglm/gglm" - "github.com/bloeys/nmage/asserts" + "github.com/bloeys/nmage/assert" "github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/timing" "github.com/go-gl/gl/v4.1-core/gl" @@ -23,7 +23,7 @@ type ImguiInfo struct { func (i *ImguiInfo) FrameStart(winWidth, winHeight float32) { if err := i.ImCtx.SetCurrent(); err != nil { - asserts.T(false, "Setting imgui ctx as current failed. Err: "+err.Error()) + assert.T(false, "Setting imgui ctx as current failed. Err: "+err.Error()) } imIO := imgui.CurrentIO() @@ -36,7 +36,7 @@ func (i *ImguiInfo) FrameStart(winWidth, winHeight float32) { func (i *ImguiInfo) Render(winWidth, winHeight float32, fbWidth, fbHeight int32) { if err := i.ImCtx.SetCurrent(); err != nil { - asserts.T(false, "Setting imgui ctx as current failed. Err: "+err.Error()) + assert.T(false, "Setting imgui ctx as current failed. Err: "+err.Error()) } imgui.Render()