Support interleaved buffers

This commit is contained in:
bloeys
2022-01-26 08:04:50 +04:00
parent e1e617e4e4
commit 1109caef43
8 changed files with 199 additions and 199 deletions

View File

@ -5,14 +5,11 @@ import (
"github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/logging"
) )
func True(check bool, msg string) { func T(check bool, msg string) {
if consts.Debug && !check {
logging.ErrLog.Panicln(msg)
}
}
func False(check bool, msg string) { if !consts.Debug || check {
if consts.Debug && check { return
logging.ErrLog.Panicln(msg)
} }
logging.ErrLog.Panicln("Assert failed:", msg)
} }

33
buffers/buf_usage.go Executable file
View File

@ -0,0 +1,33 @@
package buffers
import (
"fmt"
"github.com/bloeys/nmage/asserts"
"github.com/go-gl/gl/v4.1-core/gl"
)
type BufUsage int
const (
//Buffer is set only once and used many times
BufUsageStatic BufUsage = iota
//Buffer is changed a lot and used many times
BufUsageDynamic
//Buffer is set only once and used by the GPU at most a few times
BufUsageStream
)
func (b BufUsage) ToGL() uint32 {
switch b {
case BufUsageStatic:
return gl.STATIC_DRAW
case BufUsageDynamic:
return gl.DYNAMIC_DRAW
case BufUsageStream:
return gl.STREAM_DRAW
}
asserts.T(false, fmt.Sprintf("Unexpected BufUsage value '%v'", b))
return 0
}

View File

@ -5,169 +5,89 @@ import (
"github.com/go-gl/gl/v4.1-core/gl" "github.com/go-gl/gl/v4.1-core/gl"
) )
type BufGLType int type BufferLayoutElement struct {
Offset int
const ( DataType
BufGLTypeUnknown BufGLType = 0
//Generic array of data. Should be used for most data like vertex positions, vertex colors etc.
BufGLTypeArray BufGLType = gl.ARRAY_BUFFER
BufGLTypeIndices BufGLType = gl.ELEMENT_ARRAY_BUFFER
)
type BufType int
const (
BufTypeUnknown BufType = iota
BufTypeVertPos
BufTypeColor
BufTypeIndex
BufTypeNormal
)
func (bt BufType) GetBufferGLType() BufGLType {
switch bt {
case BufTypeNormal:
fallthrough
case BufTypeColor:
fallthrough
case BufTypeVertPos:
return BufGLTypeArray
case BufTypeIndex:
return BufGLTypeIndices
default:
logging.WarnLog.Println("Unknown BufferType. BufferType: ", bt)
return BufGLTypeUnknown
}
} }
type BufUsage int type BufferLayout struct {
Elements []BufferLayoutElement
const ( }
//Buffer is set only once and used many times
BufUsageStatic BufUsage = gl.STATIC_DRAW
//Buffer is changed a lot and used many times
BufUsageDynamic BufUsage = gl.DYNAMIC_DRAW
//Buffer is set only once and used by the GPU at most a few times
BufUsageStream BufUsage = gl.STREAM_DRAW
)
type Buffer struct { type Buffer struct {
ID uint32 VAOID uint32
Type BufType //BufID is the ID of the VBO
GLType BufGLType BufID uint32
DataTypeInfo //IndexBufID is the ID of the index/element buffer
IndexBufID uint32
//DataLen is the number of elements in the uploaded to the buffer IndexBufCount int32
DataLen int32 Layout BufferLayout
Stride int32
} }
func (b *Buffer) Activate() { func (b *Buffer) Bind() {
gl.BindBuffer(uint32(b.GLType), b.ID) gl.BindVertexArray(b.VAOID)
} }
func (b *Buffer) Deactivate() { func (b *Buffer) UnBind() {
gl.BindBuffer(uint32(b.GLType), 0)
}
type BufferObject struct {
VAOID uint32
VertPosBuf *Buffer
NormalBuf *Buffer
ColorBuf *Buffer
IndexBuf *Buffer
}
func (bo *BufferObject) GenBuffer(data []float32, bufUsage BufUsage, bufType BufType, bufDataType DataType) {
gl.BindVertexArray(bo.VAOID)
//Create vertex buffer object
var vboID uint32
gl.GenBuffers(1, &vboID)
if vboID == 0 {
logging.ErrLog.Println("Failed to create openGL buffer")
}
buf := &Buffer{
ID: vboID,
Type: bufType,
GLType: bufType.GetBufferGLType(),
DataTypeInfo: GetDataTypeInfo(bufDataType),
DataLen: int32(len(data)),
}
bo.SetBuffer(buf)
//Fill buffer with data
gl.BindBuffer(uint32(buf.GLType), buf.ID)
gl.BufferData(uint32(buf.GLType), int(buf.DataTypeInfo.ElementSize)*len(data), gl.Ptr(data), uint32(bufUsage))
//Unbind everything
gl.BindVertexArray(0)
gl.BindBuffer(uint32(buf.GLType), 0)
}
func (bo *BufferObject) GenBufferUint32(data []uint32, bufUsage BufUsage, bufType BufType, bufDataType DataType) {
gl.BindVertexArray(bo.VAOID)
//Create vertex buffer object
var vboID uint32
gl.GenBuffers(1, &vboID)
if vboID == 0 {
logging.ErrLog.Println("Failed to create openGL buffer")
}
buf := &Buffer{
ID: vboID,
Type: bufType,
GLType: bufType.GetBufferGLType(),
DataTypeInfo: GetDataTypeInfo(bufDataType),
DataLen: int32(len(data)),
}
bo.SetBuffer(buf)
//Fill buffer with data
gl.BindBuffer(uint32(buf.GLType), buf.ID)
gl.BufferData(uint32(buf.GLType), int(buf.DataTypeInfo.ElementSize)*len(data), gl.Ptr(data), uint32(bufUsage))
//Unbind everything
gl.BindVertexArray(0)
gl.BindBuffer(uint32(buf.GLType), 0)
}
func (bo *BufferObject) SetBuffer(buf *Buffer) {
switch buf.Type {
case BufTypeVertPos:
bo.VertPosBuf = buf
case BufTypeNormal:
bo.NormalBuf = buf
case BufTypeColor:
bo.ColorBuf = buf
case BufTypeIndex:
bo.IndexBuf = buf
default:
logging.WarnLog.Println("Unknown buffer type in SetBuffer. Type:", buf.Type)
}
}
func (bo *BufferObject) Bind() {
gl.BindVertexArray(bo.VAOID)
}
func (bo *BufferObject) UnBind() {
gl.BindVertexArray(0) gl.BindVertexArray(0)
} }
func NewBufferObject() *BufferObject { func (b *Buffer) CalcValues() {
var vaoID uint32 b.Stride = 0
gl.GenVertexArrays(1, &vaoID) for i := 0; i < len(b.Layout.Elements); i++ {
if vaoID == 0 {
info := GetDataTypeInfo(b.Layout.Elements[i].DataType)
b.Layout.Elements[i].Offset = int(b.Stride)
b.Stride += info.GetSize()
}
}
func (b *Buffer) SetData(values []float32) {
gl.BindVertexArray(b.VAOID)
gl.BindBuffer(gl.ARRAY_BUFFER, b.BufID)
gl.BufferData(gl.ARRAY_BUFFER, len(values)*4, gl.Ptr(values), BufUsageStatic.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)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(values)*4, gl.Ptr(values), BufUsageStatic.ToGL())
gl.BindVertexArray(0)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
}
func NewBuffer(layout BufferLayout) Buffer {
b := Buffer{
Layout: layout,
}
gl.GenVertexArrays(1, &b.VAOID)
if b.VAOID == 0 {
logging.ErrLog.Println("Failed to create openGL vertex array object") logging.ErrLog.Println("Failed to create openGL vertex array object")
} }
return &BufferObject{VAOID: vaoID} 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.CalcValues()
return b
} }

View File

@ -1,7 +1,9 @@
package buffers package buffers
import ( import (
"github.com/bloeys/nmage/logging" "fmt"
"github.com/bloeys/nmage/asserts"
"github.com/go-gl/gl/v4.1-core/gl" "github.com/go-gl/gl/v4.1-core/gl"
) )
@ -26,8 +28,6 @@ type DataTypeInfo struct {
ElementCount int32 ElementCount int32
//ElementType is the type of each primitive (e.g. for vec3 its gl.FLOAT) //ElementType is the type of each primitive (e.g. for vec3 its gl.FLOAT)
ElementType uint32 ElementType uint32
//GLType is the type of the variable represented (e.g. for vec3 its gl.FLOAT_VEC2)
GLType uint32
} }
//GetSize returns the total size in bytes (e.g. for vec3 its 4*3) //GetSize returns the total size in bytes (e.g. for vec3 its 4*3)
@ -45,7 +45,6 @@ func GetDataTypeInfo(dt DataType) DataTypeInfo {
ElementSize: 4, ElementSize: 4,
ElementCount: 1, ElementCount: 1,
ElementType: gl.INT, ElementType: gl.INT,
GLType: gl.INT,
} }
case DataTypeFloat32: case DataTypeFloat32:
@ -53,14 +52,12 @@ func GetDataTypeInfo(dt DataType) DataTypeInfo {
ElementSize: 4, ElementSize: 4,
ElementCount: 1, ElementCount: 1,
ElementType: gl.FLOAT, ElementType: gl.FLOAT,
GLType: gl.FLOAT,
} }
case DataTypeFloat64: case DataTypeFloat64:
return DataTypeInfo{ return DataTypeInfo{
ElementSize: 8, ElementSize: 8,
ElementCount: 1, ElementCount: 1,
ElementType: gl.DOUBLE, ElementType: gl.DOUBLE,
GLType: gl.DOUBLE,
} }
case DataTypeVec2: case DataTypeVec2:
@ -68,25 +65,22 @@ func GetDataTypeInfo(dt DataType) DataTypeInfo {
ElementSize: 4, ElementSize: 4,
ElementCount: 2, ElementCount: 2,
ElementType: gl.FLOAT, ElementType: gl.FLOAT,
GLType: gl.FLOAT_VEC2,
} }
case DataTypeVec3: case DataTypeVec3:
return DataTypeInfo{ return DataTypeInfo{
ElementSize: 4, ElementSize: 4,
ElementCount: 3, ElementCount: 3,
ElementType: gl.FLOAT, ElementType: gl.FLOAT,
GLType: gl.FLOAT_VEC3,
} }
case DataTypeVec4: case DataTypeVec4:
return DataTypeInfo{ return DataTypeInfo{
ElementSize: 4, ElementSize: 4,
ElementCount: 4, ElementCount: 4,
ElementType: gl.FLOAT, ElementType: gl.FLOAT,
GLType: gl.FLOAT_VEC4,
} }
default: default:
logging.WarnLog.Println("Unknown data type passed. DataType:", dt) asserts.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt))
return DataTypeInfo{} return DataTypeInfo{}
} }
} }

18
main.go
View File

@ -92,14 +92,7 @@ func main() {
initImGUI() initImGUI()
//Enable vertex attributes //Enable vertex attributes
simpleMat.SetAttribute("vertPosIn", cubeMesh.BufObj, cubeMesh.BufObj.VertPosBuf) simpleMat.SetAttribute(cubeMesh.BufObjV2)
simpleMat.EnableAttribute("vertPosIn")
simpleMat.SetAttribute("vertColorIn", cubeMesh.BufObj, cubeMesh.BufObj.ColorBuf)
simpleMat.EnableAttribute("vertColorIn")
simpleMat.SetAttribute("vertNormalIn", cubeMesh.BufObj, cubeMesh.BufObj.NormalBuf)
simpleMat.EnableAttribute("vertNormalIn")
//Movement, scale and rotation //Movement, scale and rotation
translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0)) translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0))
@ -358,23 +351,20 @@ func draw() {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
simpleMat.Bind() simpleMat.Bind()
cubeMesh.BufObj.Bind() cubeMesh.BufObjV2.Bind()
tempModelMat := modelMat.Clone() tempModelMat := modelMat.Clone()
gl.BindBuffer(gl.ARRAY_BUFFER, cubeMesh.BufObjV2.BufID)
rowSize := 10 rowSize := 10
for y := 0; y < rowSize; y++ { for y := 0; y < rowSize; y++ {
for x := 0; x < rowSize; x++ { for x := 0; x < rowSize; x++ {
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4) simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(-1, 0, 0)).Mat4)
gl.DrawElements(gl.TRIANGLES, int32(cubeMesh.BufObj.IndexBuf.DataLen), gl.UNSIGNED_INT, gl.PtrOffset(0)) gl.DrawElements(gl.TRIANGLES, cubeMesh.BufObjV2.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0))
} }
simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0)).Mat4) simpleMat.SetUnifMat4("modelMat", &tempModelMat.Translate(gglm.NewVec3(float32(rowSize), -1, 0)).Mat4)
} }
simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4) simpleMat.SetUnifMat4("modelMat", &modelMat.Mat4)
cubeMesh.BufObj.UnBind()
drawUI()
window.SDLWin.GLSwap() window.SDLWin.GLSwap()
} }

View File

@ -25,16 +25,23 @@ func (m *Material) GetAttribLoc(attribName string) int32 {
return gl.GetAttribLocation(m.ShaderProg.ID, gl.Str(attribName+"\x00")) return gl.GetAttribLocation(m.ShaderProg.ID, gl.Str(attribName+"\x00"))
} }
func (m *Material) SetAttribute(attribName string, bufObj *buffers.BufferObject, buf *buffers.Buffer) { func (m *Material) SetAttribute(bufObj buffers.Buffer) {
bufObj.Bind() bufObj.Bind()
buf.Activate()
attribLoc := m.GetAttribLoc(attribName) //NOTE: VBOs are only bound at 'VertexAttribPointer', not BindBUffer, so we need to bind the buffer and vao here
gl.VertexAttribPointer(uint32(attribLoc), buf.ElementCount, buf.ElementType, false, buf.GetSize(), gl.PtrOffset(0)) gl.BindBuffer(gl.ARRAY_BUFFER, bufObj.BufID)
bufObj.Bind() for i := 0; i < len(bufObj.Layout.Elements); i++ {
buf.Deactivate()
gl.EnableVertexAttribArray(uint32(i))
info := buffers.GetDataTypeInfo(bufObj.Layout.Elements[i].DataType)
gl.VertexAttribPointer(uint32(i), info.ElementCount, info.ElementType, false, bufObj.Stride, gl.PtrOffset(bufObj.Layout.Elements[i].Offset))
}
bufObj.UnBind()
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
} }
func (m *Material) EnableAttribute(attribName string) { func (m *Material) EnableAttribute(attribName string) {

View File

@ -11,8 +11,8 @@ import (
) )
type Mesh struct { type Mesh struct {
Name string Name string
BufObj *buffers.BufferObject BufObjV2 buffers.Buffer
} }
func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, error) { func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh, error) {
@ -27,20 +27,79 @@ func NewMesh(name, modelPath string, postProcessFlags asig.PostProcess) (*Mesh,
return nil, errors.New("No meshes found in file: " + modelPath) return nil, errors.New("No meshes found in file: " + modelPath)
} }
mesh := &Mesh{Name: name, BufObj: buffers.NewBufferObject()} mesh := &Mesh{Name: name}
sceneMesh := scene.Meshes[0] sceneMesh := scene.Meshes[0]
mesh.BufObj.GenBuffer(flattenVec3(sceneMesh.Normals), buffers.BufUsageStatic, buffers.BufTypeNormal, buffers.DataTypeVec3) dataSize := len(sceneMesh.Vertices)*3 + len(sceneMesh.Normals)*3
mesh.BufObj.GenBuffer(flattenVec3(sceneMesh.Vertices), buffers.BufUsageStatic, buffers.BufTypeVertPos, buffers.DataTypeVec3)
mesh.BufObj.GenBufferUint32(flattenFaces(sceneMesh.Faces), buffers.BufUsageStatic, buffers.BufTypeIndex, buffers.DataTypeUint32) mesh.BufObjV2 = buffers.NewBuffer(buffers.BufferLayout{
Elements: []buffers.BufferLayoutElement{
{DataType: buffers.DataTypeVec3},
{DataType: buffers.DataTypeVec3},
},
})
if len(sceneMesh.ColorSets) > 0 { if len(sceneMesh.ColorSets) > 0 {
mesh.BufObj.GenBuffer(flattenVec4(sceneMesh.ColorSets[0]), buffers.BufUsageStatic, buffers.BufTypeColor, buffers.DataTypeVec4) mesh.BufObjV2.Layout.Elements = append(mesh.BufObjV2.Layout.Elements, buffers.BufferLayoutElement{DataType: buffers.DataTypeVec4})
dataSize += len(sceneMesh.ColorSets) * 4
mesh.BufObjV2.CalcValues()
} }
positions := flattenVec3(sceneMesh.Vertices)
normals := flattenVec3(sceneMesh.Normals)
colors := []float32{}
if len(sceneMesh.ColorSets) > 0 {
colors = flattenVec4(sceneMesh.ColorSets[0])
}
var values []float32
if len(colors) > 0 {
values = interleave(
arrInfo{values: positions, valsPerComp: 3},
arrInfo{values: normals, valsPerComp: 3},
arrInfo{values: colors, valsPerComp: 4},
)
} else {
values = interleave(
arrInfo{values: positions, valsPerComp: 3},
arrInfo{values: normals, valsPerComp: 3},
)
}
mesh.BufObjV2.SetData(values)
mesh.BufObjV2.SetIndexBufData(flattenFaces(sceneMesh.Faces))
return mesh, nil return mesh, nil
} }
type arrInfo struct {
values []float32
valsPerComp int
}
func interleave(arrs ...arrInfo) []float32 {
if len(arrs) == 0 || len(arrs[0].values) == 0 {
panic("No input to interleave or arrays are empty")
}
size := 0
for i := 0; i < len(arrs); i++ {
size += len(arrs[i].values)
}
out := make([]float32, 0, size)
for posInArr := 0; posInArr < len(arrs[0].values)/arrs[0].valsPerComp; posInArr++ {
for arrToUse := 0; arrToUse < len(arrs); arrToUse++ {
for compToAdd := 0; compToAdd < arrs[arrToUse].valsPerComp; compToAdd++ {
out = append(out, arrs[arrToUse].values[posInArr*arrs[arrToUse].valsPerComp+compToAdd])
}
}
}
return out
}
func flattenVec3(vec3s []gglm.Vec3) []float32 { func flattenVec3(vec3s []gglm.Vec3) []float32 {
floats := make([]float32, len(vec3s)*3) floats := make([]float32, len(vec3s)*3)
@ -68,7 +127,7 @@ func flattenVec4(vec4s []gglm.Vec4) []float32 {
func flattenFaces(faces []asig.Face) []uint32 { func flattenFaces(faces []asig.Face) []uint32 {
asserts.True(len(faces[0].Indices) == 3, fmt.Sprintf("Face doesn't have 3 indices. Index count: %v\n", len(faces[0].Indices))) asserts.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) uints := make([]uint32, len(faces)*3)
for i := 0; i < len(faces); i++ { for i := 0; i < len(faces); i++ {

View File

@ -1,8 +1,8 @@
#version 410 #version 410
in vec3 vertPosIn; layout(location=0) in vec3 vertPosIn;
in vec3 vertColorIn; layout(location=1) in vec3 vertNormalIn;
in vec3 vertNormalIn; layout(location=2) in vec3 vertColorIn;
out vec3 vertColor; out vec3 vertColor;
out vec3 vertNormal; out vec3 vertNormal;