Split buffer struct into VAO+VBO+IBO structs

This commit is contained in:
bloeys
2024-04-13 02:59:31 +04:00
parent 524ef068f0
commit 692167ada2
8 changed files with 193 additions and 148 deletions

View File

@ -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
}

46
buffers/index_buffer.go Executable file
View File

@ -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
}

54
buffers/vertex_array.go Executable file
View File

@ -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
}

63
buffers/vertex_buffer.go Executable file
View File

@ -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
}

View File

@ -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)

View File

@ -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++ {

View File

@ -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
}

Binary file not shown.