diff --git a/asserts/asserts.go b/asserts/asserts.go index 979d64e..81f551c 100755 --- a/asserts/asserts.go +++ b/asserts/asserts.go @@ -5,14 +5,11 @@ import ( "github.com/bloeys/nmage/logging" ) -func True(check bool, msg string) { - if consts.Debug && !check { - logging.ErrLog.Panicln(msg) - } -} +func T(check bool, msg string) { -func False(check bool, msg string) { - if consts.Debug && check { - logging.ErrLog.Panicln(msg) + if !consts.Debug || check { + return } + + logging.ErrLog.Panicln("Assert failed:", msg) } diff --git a/buffers/buf_usage.go b/buffers/buf_usage.go new file mode 100755 index 0000000..2039d28 --- /dev/null +++ b/buffers/buf_usage.go @@ -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 +} diff --git a/buffers/buffers.go b/buffers/buffers.go index 1c89edb..fd56daf 100755 --- a/buffers/buffers.go +++ b/buffers/buffers.go @@ -5,169 +5,89 @@ import ( "github.com/go-gl/gl/v4.1-core/gl" ) -type BufGLType int - -const ( - 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 BufferLayoutElement struct { + Offset int + DataType } -type BufUsage int - -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 BufferLayout struct { + Elements []BufferLayoutElement +} type Buffer struct { - ID uint32 - Type BufType - GLType BufGLType - DataTypeInfo - - //DataLen is the number of elements in the uploaded to the buffer - DataLen int32 + VAOID uint32 + //BufID is the ID of the VBO + BufID uint32 + //IndexBufID is the ID of the index/element buffer + IndexBufID uint32 + IndexBufCount int32 + Layout BufferLayout + Stride int32 } -func (b *Buffer) Activate() { - gl.BindBuffer(uint32(b.GLType), b.ID) +func (b *Buffer) Bind() { + gl.BindVertexArray(b.VAOID) } -func (b *Buffer) Deactivate() { - 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() { +func (b *Buffer) UnBind() { gl.BindVertexArray(0) } -func NewBufferObject() *BufferObject { +func (b *Buffer) CalcValues() { - var vaoID uint32 - gl.GenVertexArrays(1, &vaoID) - if vaoID == 0 { + b.Stride = 0 + for i := 0; i < len(b.Layout.Elements); i++ { + + 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") } - 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 } diff --git a/buffers/dataType.go b/buffers/dataType.go index b4965d8..c2acb1b 100755 --- a/buffers/dataType.go +++ b/buffers/dataType.go @@ -1,7 +1,9 @@ package buffers import ( - "github.com/bloeys/nmage/logging" + "fmt" + + "github.com/bloeys/nmage/asserts" "github.com/go-gl/gl/v4.1-core/gl" ) @@ -26,8 +28,6 @@ type DataTypeInfo struct { ElementCount int32 //ElementType is the type of each primitive (e.g. for vec3 its gl.FLOAT) 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) @@ -45,7 +45,6 @@ func GetDataTypeInfo(dt DataType) DataTypeInfo { ElementSize: 4, ElementCount: 1, ElementType: gl.INT, - GLType: gl.INT, } case DataTypeFloat32: @@ -53,14 +52,12 @@ func GetDataTypeInfo(dt DataType) DataTypeInfo { ElementSize: 4, ElementCount: 1, ElementType: gl.FLOAT, - GLType: gl.FLOAT, } case DataTypeFloat64: return DataTypeInfo{ ElementSize: 8, ElementCount: 1, ElementType: gl.DOUBLE, - GLType: gl.DOUBLE, } case DataTypeVec2: @@ -68,25 +65,22 @@ func GetDataTypeInfo(dt DataType) DataTypeInfo { ElementSize: 4, ElementCount: 2, ElementType: gl.FLOAT, - GLType: gl.FLOAT_VEC2, } case DataTypeVec3: return DataTypeInfo{ ElementSize: 4, ElementCount: 3, ElementType: gl.FLOAT, - GLType: gl.FLOAT_VEC3, } case DataTypeVec4: return DataTypeInfo{ ElementSize: 4, ElementCount: 4, ElementType: gl.FLOAT, - GLType: gl.FLOAT_VEC4, } default: - logging.WarnLog.Println("Unknown data type passed. DataType:", dt) + asserts.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt)) return DataTypeInfo{} } } diff --git a/main.go b/main.go index a24c63d..5904645 100755 --- a/main.go +++ b/main.go @@ -92,14 +92,7 @@ func main() { initImGUI() //Enable vertex attributes - simpleMat.SetAttribute("vertPosIn", cubeMesh.BufObj, cubeMesh.BufObj.VertPosBuf) - 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") + simpleMat.SetAttribute(cubeMesh.BufObjV2) //Movement, scale and rotation translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0)) @@ -358,23 +351,20 @@ func draw() { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) simpleMat.Bind() - cubeMesh.BufObj.Bind() + cubeMesh.BufObjV2.Bind() tempModelMat := modelMat.Clone() + gl.BindBuffer(gl.ARRAY_BUFFER, cubeMesh.BufObjV2.BufID) rowSize := 10 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, 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", &modelMat.Mat4) - cubeMesh.BufObj.UnBind() - - drawUI() - window.SDLWin.GLSwap() } diff --git a/materials/material.go b/materials/material.go index cb955a7..7fcd552 100755 --- a/materials/material.go +++ b/materials/material.go @@ -25,16 +25,23 @@ func (m *Material) GetAttribLoc(attribName string) int32 { 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() - buf.Activate() - attribLoc := m.GetAttribLoc(attribName) - gl.VertexAttribPointer(uint32(attribLoc), buf.ElementCount, buf.ElementType, false, buf.GetSize(), gl.PtrOffset(0)) + //NOTE: VBOs are only bound at 'VertexAttribPointer', not BindBUffer, so we need to bind the buffer and vao here + gl.BindBuffer(gl.ARRAY_BUFFER, bufObj.BufID) - bufObj.Bind() - buf.Deactivate() + for i := 0; i < len(bufObj.Layout.Elements); i++ { + + 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) { diff --git a/meshes/mesh.go b/meshes/mesh.go index f8fdc01..61b00e4 100755 --- a/meshes/mesh.go +++ b/meshes/mesh.go @@ -11,8 +11,8 @@ import ( ) type Mesh struct { - Name string - BufObj *buffers.BufferObject + Name string + BufObjV2 buffers.Buffer } 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) } - mesh := &Mesh{Name: name, BufObj: buffers.NewBufferObject()} + mesh := &Mesh{Name: name} sceneMesh := scene.Meshes[0] - mesh.BufObj.GenBuffer(flattenVec3(sceneMesh.Normals), buffers.BufUsageStatic, buffers.BufTypeNormal, buffers.DataTypeVec3) - mesh.BufObj.GenBuffer(flattenVec3(sceneMesh.Vertices), buffers.BufUsageStatic, buffers.BufTypeVertPos, buffers.DataTypeVec3) - mesh.BufObj.GenBufferUint32(flattenFaces(sceneMesh.Faces), buffers.BufUsageStatic, buffers.BufTypeIndex, buffers.DataTypeUint32) + dataSize := len(sceneMesh.Vertices)*3 + len(sceneMesh.Normals)*3 + + mesh.BufObjV2 = buffers.NewBuffer(buffers.BufferLayout{ + Elements: []buffers.BufferLayoutElement{ + {DataType: buffers.DataTypeVec3}, + {DataType: buffers.DataTypeVec3}, + }, + }) 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 } +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 { floats := make([]float32, len(vec3s)*3) @@ -68,7 +127,7 @@ func flattenVec4(vec4s []gglm.Vec4) []float32 { 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) for i := 0; i < len(faces); i++ { diff --git a/res/shaders/simple.vert.glsl b/res/shaders/simple.vert.glsl index 00b54ef..3b98682 100755 --- a/res/shaders/simple.vert.glsl +++ b/res/shaders/simple.vert.glsl @@ -1,8 +1,8 @@ #version 410 -in vec3 vertPosIn; -in vec3 vertColorIn; -in vec3 vertNormalIn; +layout(location=0) in vec3 vertPosIn; +layout(location=1) in vec3 vertNormalIn; +layout(location=2) in vec3 vertColorIn; out vec3 vertColor; out vec3 vertNormal;