diff --git a/buffers/buffers.go b/buffers/buffers.go deleted file mode 100755 index 7ef83d4..0000000 --- a/buffers/buffers.go +++ /dev/null @@ -1,134 +0,0 @@ -package buffers - -import ( - "github.com/bloeys/nmage/logging" - "github.com/go-gl/gl/v4.1-core/gl" -) - -type Buffer struct { - VAOID uint32 - // BufID is the ID of the VBO - BufID uint32 - // IndexBufID is the ID of the index/element buffer - IndexBufID uint32 - // IndexBufCount is the number of elements in the index buffer - // Updated on SetIndexBufData - IndexBufCount int32 - // IndexBufCount int32 - Stride int32 - - layout []Element -} - -func (b *Buffer) Bind() { - gl.BindVertexArray(b.VAOID) -} - -func (b *Buffer) UnBind() { - gl.BindVertexArray(0) -} - -func (b *Buffer) SetData(values []float32) { - - gl.BindVertexArray(b.VAOID) - gl.BindBuffer(gl.ARRAY_BUFFER, b.BufID) - - 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.BindBuffer(gl.ARRAY_BUFFER, 0) -} - -func (b *Buffer) SetDataWithUsage(values []float32, usage BufUsage) { - - gl.BindVertexArray(b.VAOID) - gl.BindBuffer(gl.ARRAY_BUFFER, b.BufID) - - sizeInBytes := len(values) * 4 - if sizeInBytes == 0 { - gl.BufferData(gl.ARRAY_BUFFER, 0, gl.Ptr(nil), usage.ToGL()) - } else { - gl.BufferData(gl.ARRAY_BUFFER, sizeInBytes, gl.Ptr(&values[0]), usage.ToGL()) - } - - gl.BindVertexArray(0) - gl.BindBuffer(gl.ARRAY_BUFFER, 0) -} - -func (b *Buffer) SetIndexBufData(values []uint32) { - - b.IndexBufCount = int32(len(values)) - gl.BindVertexArray(b.VAOID) - gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.IndexBufID) - - 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.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0) -} - -func (b *Buffer) GetLayout() []Element { - e := make([]Element, len(b.layout)) - copy(e, b.layout) - return e -} - -// SetLayout updates the layout object and the corresponding vertex attributes. -// Vertex attributes are also enabled. -func (b *Buffer) SetLayout(layout ...Element) { - - b.layout = layout - - b.Stride = 0 - for i := 0; i < len(b.layout); i++ { - - b.layout[i].Offset = int(b.Stride) - 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.VertexAttribPointerWithOffset(uint32(i), layout[i].ElementType.CompCount(), layout[i].ElementType.GLType(), false, b.Stride, uintptr(layout[i].Offset)) - } - - b.UnBind() - gl.BindBuffer(gl.ARRAY_BUFFER, 0) -} - -func NewBuffer(layout ...Element) Buffer { - - b := Buffer{} - gl.GenVertexArrays(1, &b.VAOID) - if b.VAOID == 0 { - logging.ErrLog.Println("Failed to create openGL vertex array object") - } - - gl.GenBuffers(1, &b.BufID) - if b.BufID == 0 { - logging.ErrLog.Println("Failed to create openGL buffer") - } - - gl.GenBuffers(1, &b.IndexBufID) - if b.IndexBufID == 0 { - logging.ErrLog.Println("Failed to create openGL buffer") - } - - b.SetLayout(layout...) - return b -} diff --git a/buffers/index_buffer.go b/buffers/index_buffer.go new file mode 100755 index 0000000..f99dd03 --- /dev/null +++ b/buffers/index_buffer.go @@ -0,0 +1,46 @@ +package buffers + +import ( + "github.com/bloeys/nmage/logging" + "github.com/go-gl/gl/v4.1-core/gl" +) + +type IndexBuffer struct { + Id uint32 + // IndexBufCount is the number of elements in the index buffer. Updated in IndexBuffer.SetData + IndexBufCount int32 +} + +func (ib *IndexBuffer) Bind() { + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ib.Id) +} + +func (ib *IndexBuffer) UnBind() { + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0) +} + +func (ib *IndexBuffer) SetData(values []uint32) { + + ib.Bind() + + sizeInBytes := len(values) * 4 + ib.IndexBufCount = int32(len(values)) + + 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()) + } +} + +func NewIndexBuffer() IndexBuffer { + + ib := IndexBuffer{} + + gl.GenBuffers(1, &ib.Id) + if ib.Id == 0 { + logging.ErrLog.Println("Failed to create OpenGL buffer") + } + + return ib +} diff --git a/buffers/vertex_array.go b/buffers/vertex_array.go new file mode 100755 index 0000000..fd4be04 --- /dev/null +++ b/buffers/vertex_array.go @@ -0,0 +1,54 @@ +package buffers + +import ( + "github.com/bloeys/nmage/logging" + "github.com/go-gl/gl/v4.1-core/gl" +) + +type VertexArray struct { + Id uint32 + Vbos []VertexBuffer + IndexBuffer IndexBuffer +} + +func (va *VertexArray) Bind() { + gl.BindVertexArray(va.Id) +} + +func (va *VertexArray) UnBind() { + gl.BindVertexArray(0) +} + +func (va *VertexArray) AddVertexBuffer(vbo VertexBuffer) { + + // NOTE: VBOs are only bound at 'VertexAttribPointer' (and related) calls + + va.Bind() + vbo.Bind() + + for i := 0; i < len(vbo.layout); i++ { + + l := &vbo.layout[i] + + gl.EnableVertexAttribArray(uint32(i)) + gl.VertexAttribPointerWithOffset(uint32(i), l.ElementType.CompCount(), l.ElementType.GLType(), false, vbo.Stride, uintptr(l.Offset)) + } +} + +func (va *VertexArray) SetIndexBuffer(ib IndexBuffer) { + va.Bind() + ib.Bind() + va.IndexBuffer = ib +} + +func NewVertexArray() VertexArray { + + vao := VertexArray{} + + gl.GenVertexArrays(1, &vao.Id) + if vao.Id == 0 { + logging.ErrLog.Println("Failed to create OpenGL vertex array object") + } + + return vao +} diff --git a/buffers/vertex_buffer.go b/buffers/vertex_buffer.go new file mode 100755 index 0000000..27144a4 --- /dev/null +++ b/buffers/vertex_buffer.go @@ -0,0 +1,63 @@ +package buffers + +import ( + "github.com/bloeys/nmage/logging" + "github.com/go-gl/gl/v4.1-core/gl" +) + +type VertexBuffer struct { + Id uint32 + Stride int32 + layout []Element +} + +func (vb *VertexBuffer) Bind() { + gl.BindBuffer(gl.ARRAY_BUFFER, vb.Id) +} + +func (vb *VertexBuffer) UnBind() { + gl.BindBuffer(gl.ARRAY_BUFFER, 0) +} + +func (vb *VertexBuffer) SetData(values []float32, usage BufUsage) { + + vb.Bind() + + sizeInBytes := len(values) * 4 + if sizeInBytes == 0 { + gl.BufferData(gl.ARRAY_BUFFER, 0, gl.Ptr(nil), usage.ToGL()) + } else { + gl.BufferData(gl.ARRAY_BUFFER, sizeInBytes, gl.Ptr(&values[0]), usage.ToGL()) + } +} + +func (vb *VertexBuffer) GetLayout() []Element { + e := make([]Element, len(vb.layout)) + copy(e, vb.layout) + return e +} + +func (vb *VertexBuffer) SetLayout(layout ...Element) { + + vb.Stride = 0 + vb.layout = layout + + for i := 0; i < len(vb.layout); i++ { + + vb.layout[i].Offset = int(vb.Stride) + vb.Stride += vb.layout[i].Size() + } +} + +func NewVertexBuffer(layout ...Element) VertexBuffer { + + vb := VertexBuffer{} + + gl.GenBuffers(1, &vb.Id) + if vb.Id == 0 { + logging.ErrLog.Println("Failed to create OpenGL buffer") + } + + vb.SetLayout(layout...) + return vb +} diff --git a/main.go b/main.go index b005a5c..d008ce8 100755 --- a/main.go +++ b/main.go @@ -34,7 +34,7 @@ import ( - Cascaded shadow mapping - Skeletal animations - Proper model loading (i.e. load model by reading all its meshes, textures, and so on together) - - Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing) + - Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing) ✅ - Renderer batching - Scene graph - Separate engine loop from rendering loop? or leave it to the user? @@ -812,7 +812,7 @@ func (g *Game) DrawSkybox() { gl.Disable(gl.CULL_FACE) gl.DepthFunc(gl.LEQUAL) - skyboxMesh.Buf.Bind() + skyboxMesh.Vao.Bind() skyboxMat.Bind() gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_CUBE_MAP, skyboxCmap.TexID) diff --git a/meshes/mesh.go b/meshes/mesh.go index b66e2c3..7c50a09 100755 --- a/meshes/mesh.go +++ b/meshes/mesh.go @@ -2,7 +2,6 @@ package meshes import ( "errors" - "fmt" "github.com/bloeys/assimp-go/asig" "github.com/bloeys/gglm/gglm" @@ -18,7 +17,7 @@ type SubMesh struct { type Mesh struct { Name string - Buf buffers.Buffer + Vao buffers.VertexArray SubMeshes []SubMesh } @@ -36,21 +35,25 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, mesh := &Mesh{ Name: name, - Buf: buffers.NewBuffer(), + Vao: buffers.NewVertexArray(), SubMeshes: make([]SubMesh, 0, 1), } + vbo := buffers.NewVertexBuffer() + ibo := buffers.NewIndexBuffer() + // Initial sizes assuming one submesh that has vertex pos+normals+texCoords, and 3 indices per face var vertexBufData []float32 = make([]float32, 0, len(scene.Meshes[0].Vertices)*3*3*2) var indexBufData []uint32 = make([]uint32, 0, len(scene.Meshes[0].Faces)*3) + // fmt.Printf("\nMesh %s has %d meshe(s) with first mesh having %d vertices\n", name, len(scene.Meshes), len(scene.Meshes[0].Vertices)) + for i := 0; i < len(scene.Meshes); i++ { sceneMesh := scene.Meshes[i] if len(sceneMesh.TexCoords[0]) == 0 { sceneMesh.TexCoords[0] = make([]gglm.Vec3, len(sceneMesh.Vertices)) - println("Zeroing tex coords for submesh", i) } layoutToUse := []buffers.Element{{ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec3}, {ElementType: buffers.DataTypeVec2}} @@ -59,10 +62,16 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, } if i == 0 { - mesh.Buf.SetLayout(layoutToUse...) + vbo.SetLayout(layoutToUse...) } else { - firstSubmeshLayout := mesh.Buf.GetLayout() + // @TODO @NOTE: This requirement is because we are using one VAO+VBO for all + // the meshes and so the buffer must have one format. + // + // If we want to allow different layouts then we can simply create one vbo per layout and put + // meshes of the same layout in the same vbo, and we store the index of the vbo the mesh + // uses in the submesh struct. + firstSubmeshLayout := vbo.GetLayout() assert.T(len(firstSubmeshLayout) == len(layoutToUse), "Vertex layout of submesh '%d' of mesh '%s' at path '%s' does not equal vertex layout of the first submesh. Original layout: %v; This layout: %v", i, name, modelPath, firstSubmeshLayout, layoutToUse) for i := 0; i < len(firstSubmeshLayout); i++ { @@ -79,7 +88,7 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, mesh.SubMeshes = append(mesh.SubMeshes, SubMesh{ // Index of the vertex to start from (e.g. if index buffer says use vertex 5, and BaseVertex=3, the vertex used will be vertex 8) - BaseVertex: int32(len(vertexBufData)*4) / mesh.Buf.Stride, + BaseVertex: int32(len(vertexBufData)*4) / vbo.Stride, // Which index (in the index buffer) to start from BaseIndex: uint32(len(indexBufData)), // How many indices in this submesh @@ -90,9 +99,16 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, indexBufData = append(indexBufData, indices...) } - // fmt.Printf("!!! Vertex count: %d; Submeshes: %+v\n", len(vertexBufData)*4/int(mesh.Buf.Stride), mesh.SubMeshes) - mesh.Buf.SetData(vertexBufData) - mesh.Buf.SetIndexBufData(indexBufData) + vbo.SetData(vertexBufData, buffers.BufUsage_Static) + ibo.SetData(indexBufData) + + mesh.Vao.AddVertexBuffer(vbo) + mesh.Vao.SetIndexBuffer(ibo) + + // This is needed so that if you load meshes one after the other the + // following mesh doesn't attach its vbo/ibo to this vao + mesh.Vao.UnBind() + return mesh, nil } @@ -170,7 +186,7 @@ func interleave(arrs ...arrToInterleave) []float32 { func flattenFaces(faces []asig.Face) []uint32 { - assert.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, "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/renderer/rend3dgl/rend3dgl.go b/renderer/rend3dgl/rend3dgl.go index a90ad95..be6d200 100755 --- a/renderer/rend3dgl/rend3dgl.go +++ b/renderer/rend3dgl/rend3dgl.go @@ -18,7 +18,7 @@ type Rend3DGL struct { func (r3d *Rend3DGL) Draw(mesh *meshes.Mesh, trMat *gglm.TrMat, mat *materials.Material) { if mesh != r3d.BoundMesh { - mesh.Buf.Bind() + mesh.Vao.Bind() r3d.BoundMesh = mesh } diff --git a/res/models/sphere.fbx b/res/models/sphere.fbx index 6425e7c..52cfc6e 100755 Binary files a/res/models/sphere.fbx and b/res/models/sphere.fbx differ