mirror of
https://github.com/bloeys/nmage.git
synced 2025-12-29 13:28:20 +00:00
Struct to ubo support
This commit is contained in:
@ -1,6 +1,9 @@
|
|||||||
package buffers
|
package buffers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/bloeys/gglm/gglm"
|
"github.com/bloeys/gglm/gglm"
|
||||||
"github.com/bloeys/nmage/assert"
|
"github.com/bloeys/nmage/assert"
|
||||||
"github.com/bloeys/nmage/logging"
|
"github.com/bloeys/nmage/logging"
|
||||||
@ -141,6 +144,217 @@ func (ub *UniformBuffer) SetMat4(fieldId uint16, val *gglm.Mat4) {
|
|||||||
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*16, gl.Ptr(&val.Data[0][0]))
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*16, gl.Ptr(&val.Data[0][0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetStruct(inputStruct any) {
|
||||||
|
|
||||||
|
if inputStruct == nil {
|
||||||
|
logging.ErrLog.Panicf("UniformBuffer.SetStruct called with a value that is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
structVal := reflect.ValueOf(inputStruct)
|
||||||
|
if structVal.Kind() != reflect.Struct {
|
||||||
|
logging.ErrLog.Panicf("UniformBuffer.SetStruct called with a value that is not a struct. Val=%v\n", inputStruct)
|
||||||
|
}
|
||||||
|
|
||||||
|
if structVal.NumField() != len(ub.Fields) {
|
||||||
|
logging.ErrLog.Panicf("struct fields must match uniform buffer fields, but uniform buffer contains %d fields, while the passed struct has %d fields\n", len(ub.Fields), structVal.NumField())
|
||||||
|
}
|
||||||
|
|
||||||
|
writeIndex := 0
|
||||||
|
buf := make([]byte, ub.Size)
|
||||||
|
for i := 0; i < len(ub.Fields); i++ {
|
||||||
|
|
||||||
|
ubField := &ub.Fields[i]
|
||||||
|
valField := structVal.Field(i)
|
||||||
|
|
||||||
|
if valField.Kind() == reflect.Pointer {
|
||||||
|
valField = valField.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
typeMatches := false
|
||||||
|
writeIndex = int(ubField.AlignedOffset)
|
||||||
|
|
||||||
|
switch ubField.Type {
|
||||||
|
|
||||||
|
case DataTypeUint32:
|
||||||
|
t := valField.Type()
|
||||||
|
typeMatches = t.Name() == "uint32"
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
Write32BitIntegerToByteBuf(buf, &writeIndex, uint32(valField.Uint()))
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataTypeFloat32:
|
||||||
|
t := valField.Type()
|
||||||
|
typeMatches = t.Name() == "float32"
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
WriteF32ToByteBuf(buf, &writeIndex, float32(valField.Float()))
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataTypeInt32:
|
||||||
|
t := valField.Type()
|
||||||
|
typeMatches = t.Name() == "int32"
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
Write32BitIntegerToByteBuf(buf, &writeIndex, uint32(valField.Int()))
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataTypeVec2:
|
||||||
|
|
||||||
|
v2, ok := valField.Interface().(gglm.Vec2)
|
||||||
|
typeMatches = ok
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, v2.Data[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataTypeVec3:
|
||||||
|
v3, ok := valField.Interface().(gglm.Vec3)
|
||||||
|
typeMatches = ok
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, v3.Data[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataTypeVec4:
|
||||||
|
v4, ok := valField.Interface().(gglm.Vec4)
|
||||||
|
typeMatches = ok
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, v4.Data[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataTypeMat2:
|
||||||
|
m2, ok := valField.Interface().(gglm.Mat2)
|
||||||
|
typeMatches = ok
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m2.Data[0][:])
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m2.Data[1][:])
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataTypeMat3:
|
||||||
|
m3, ok := valField.Interface().(gglm.Mat3)
|
||||||
|
typeMatches = ok
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m3.Data[0][:])
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m3.Data[1][:])
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m3.Data[2][:])
|
||||||
|
}
|
||||||
|
|
||||||
|
case DataTypeMat4:
|
||||||
|
m4, ok := valField.Interface().(gglm.Mat4)
|
||||||
|
typeMatches = ok
|
||||||
|
|
||||||
|
if typeMatches {
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m4.Data[0][:])
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m4.Data[1][:])
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m4.Data[2][:])
|
||||||
|
WriteF32SliceToByteBuf(buf, &writeIndex, m4.Data[3][:])
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert.T(false, "Unknown uniform buffer data type passed. DataType '%d'", ubField.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !typeMatches {
|
||||||
|
logging.ErrLog.Panicf("Struct field ordering and types must match uniform buffer fields, but at field index %d got UniformBufferField=%v but a struct field of %s\n", i, ubField, valField.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if writeIndex == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, 0, writeIndex, gl.Ptr(&buf[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Write32BitIntegerToByteBuf[T uint32 | int32](buf []byte, startIndex *int, val T) {
|
||||||
|
|
||||||
|
assert.T(*startIndex+4 <= len(buf), "failed to write uint32/int32 to buffer because the buffer doesn't have enough space. Start index=%d, Buffer length=%d", *startIndex, len(buf))
|
||||||
|
|
||||||
|
buf[*startIndex] = byte(val)
|
||||||
|
buf[*startIndex+1] = byte(val >> 8)
|
||||||
|
buf[*startIndex+2] = byte(val >> 16)
|
||||||
|
buf[*startIndex+3] = byte(val >> 24)
|
||||||
|
|
||||||
|
*startIndex += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteF32ToByteBuf(buf []byte, startIndex *int, val float32) {
|
||||||
|
|
||||||
|
assert.T(*startIndex+4 <= len(buf), "failed to write float32 to buffer because the buffer doesn't have enough space. Start index=%d, Buffer length=%d", *startIndex, len(buf))
|
||||||
|
|
||||||
|
bits := math.Float32bits(val)
|
||||||
|
|
||||||
|
buf[*startIndex] = byte(bits)
|
||||||
|
buf[*startIndex+1] = byte(bits >> 8)
|
||||||
|
buf[*startIndex+2] = byte(bits >> 16)
|
||||||
|
buf[*startIndex+3] = byte(bits >> 24)
|
||||||
|
|
||||||
|
*startIndex += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteF32SliceToByteBuf(buf []byte, startIndex *int, vals []float32) {
|
||||||
|
|
||||||
|
assert.T(*startIndex+4 <= len(buf), "failed to write slice of float32 to buffer because the buffer doesn't have enough space. Start index=%d, Buffer length=%d, but needs %d bytes free", *startIndex, len(buf), len(vals)*4)
|
||||||
|
|
||||||
|
for i := 0; i < len(vals); i++ {
|
||||||
|
|
||||||
|
bits := math.Float32bits(vals[i])
|
||||||
|
|
||||||
|
buf[*startIndex] = byte(bits)
|
||||||
|
buf[*startIndex+1] = byte(bits >> 8)
|
||||||
|
buf[*startIndex+2] = byte(bits >> 16)
|
||||||
|
buf[*startIndex+3] = byte(bits >> 24)
|
||||||
|
|
||||||
|
*startIndex += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReflectValueMatchesUniformBufferField(v reflect.Value, ubField *UniformBufferField) bool {
|
||||||
|
|
||||||
|
if v.Kind() == reflect.Pointer {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ubField.Type {
|
||||||
|
|
||||||
|
case DataTypeUint32:
|
||||||
|
t := v.Type()
|
||||||
|
return t.Name() == "uint32"
|
||||||
|
case DataTypeFloat32:
|
||||||
|
t := v.Type()
|
||||||
|
return t.Name() == "float32"
|
||||||
|
case DataTypeInt32:
|
||||||
|
t := v.Type()
|
||||||
|
return t.Name() == "int32"
|
||||||
|
case DataTypeVec2:
|
||||||
|
_, ok := v.Interface().(gglm.Vec2)
|
||||||
|
return ok
|
||||||
|
case DataTypeVec3:
|
||||||
|
_, ok := v.Interface().(gglm.Vec3)
|
||||||
|
return ok
|
||||||
|
case DataTypeVec4:
|
||||||
|
_, ok := v.Interface().(gglm.Vec4)
|
||||||
|
return ok
|
||||||
|
case DataTypeMat2:
|
||||||
|
_, ok := v.Interface().(gglm.Mat2)
|
||||||
|
return ok
|
||||||
|
case DataTypeMat3:
|
||||||
|
_, ok := v.Interface().(gglm.Mat3)
|
||||||
|
return ok
|
||||||
|
case DataTypeMat4:
|
||||||
|
_, ok := v.Interface().(gglm.Mat4)
|
||||||
|
return ok
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert.T(false, "Unknown uniform buffer data type passed. DataType '%d'", ubField.Type)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewUniformBuffer(fields []UniformBufferFieldInput) UniformBuffer {
|
func NewUniformBuffer(fields []UniformBufferFieldInput) UniformBuffer {
|
||||||
|
|
||||||
ub := UniformBuffer{}
|
ub := UniformBuffer{}
|
||||||
|
|||||||
75
main.go
75
main.go
@ -671,41 +671,64 @@ func (g *Game) Init() {
|
|||||||
updateAllProjViewMats(cam.ProjMat, cam.ViewMat)
|
updateAllProjViewMats(cam.ProjMat, cam.ViewMat)
|
||||||
|
|
||||||
// Ubos
|
// Ubos
|
||||||
// testUbos()
|
testUbos()
|
||||||
}
|
}
|
||||||
|
|
||||||
// func testUbos() {
|
func testUbos() {
|
||||||
|
|
||||||
// ubo := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{
|
ubo := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{
|
||||||
// {Id: 0, Type: buffers.DataTypeFloat32}, // 04 00
|
{Id: 0, Type: buffers.DataTypeFloat32}, // 04 00
|
||||||
// {Id: 1, Type: buffers.DataTypeVec3}, // 16 16
|
{Id: 1, Type: buffers.DataTypeVec3}, // 16 16
|
||||||
// {Id: 2, Type: buffers.DataTypeFloat32}, // 04 32
|
{Id: 2, Type: buffers.DataTypeFloat32}, // 04 32
|
||||||
// {Id: 3, Type: buffers.DataTypeMat2}, // 32 48
|
{Id: 3, Type: buffers.DataTypeMat2}, // 32 48
|
||||||
// }) // Total size: 48+32 = 80
|
}) // Total size: 48+32 = 80
|
||||||
|
|
||||||
// println("!!!!!!!!!!!!! Id:", ubo.Id, "; Size:", ubo.Size)
|
println("!!!!!!!!!!!!! Id:", ubo.Id, "; Size:", ubo.Size)
|
||||||
// fmt.Printf("%+v\n", ubo.Fields)
|
fmt.Printf("%+v\n", ubo.Fields)
|
||||||
|
|
||||||
// ubo.Bind()
|
ubo.Bind()
|
||||||
// ubo.SetFloat32(0, 99)
|
ubo.SetFloat32(0, 99)
|
||||||
// ubo.SetFloat32(2, 199)
|
ubo.SetFloat32(2, 199)
|
||||||
// ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{33, 33, 33}})
|
ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{33, 33, 33}})
|
||||||
|
|
||||||
// ubo.SetMat2(3, &gglm.Mat2{Data: [2][2]float32{{1, 3}, {2, 4}}})
|
ubo.SetMat2(3, &gglm.Mat2{Data: [2][2]float32{{1, 3}, {2, 4}}})
|
||||||
|
|
||||||
// var v gglm.Vec3
|
var v gglm.Vec3
|
||||||
// var m2 gglm.Mat2
|
var m2 gglm.Mat2
|
||||||
// var x, x2 float32
|
var x, x2 float32
|
||||||
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 0, 4, gl.Ptr(&x))
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 0, 4, gl.Ptr(&x))
|
||||||
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32, 4, gl.Ptr(&x2))
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32, 4, gl.Ptr(&x2))
|
||||||
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
|
||||||
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 48, 16, gl.Ptr(&m2.Data[0][0]))
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 48, 16, gl.Ptr(&m2.Data[0][0]))
|
||||||
|
|
||||||
// fmt.Printf("x=%f; x2=%f; v3=%s; m2=%s\n", x, x2, v.String(), m2.String())
|
fmt.Printf("x=%f; x2=%f; v3=%s; m2=%s\n", x, x2, v.String(), m2.String())
|
||||||
|
|
||||||
// ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{-123, 33, 33}})
|
ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{-123, 33, 33}})
|
||||||
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
|
||||||
// }
|
|
||||||
|
type TestUBO struct {
|
||||||
|
FirstF32 float32
|
||||||
|
V3 gglm.Vec3
|
||||||
|
SecondF32 float32
|
||||||
|
M2 gglm.Mat2
|
||||||
|
}
|
||||||
|
|
||||||
|
s := TestUBO{
|
||||||
|
FirstF32: 1.5,
|
||||||
|
V3: gglm.Vec3{Data: [3]float32{11, 22, 33}},
|
||||||
|
SecondF32: 9.5,
|
||||||
|
M2: gglm.Mat2{Data: [2][2]float32{{6, 8}, {7, 9}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
ubo.SetStruct(s)
|
||||||
|
|
||||||
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 0, 4, gl.Ptr(&x))
|
||||||
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32, 4, gl.Ptr(&x2))
|
||||||
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
|
||||||
|
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 48, 16, gl.Ptr(&m2.Data[0][0]))
|
||||||
|
|
||||||
|
fmt.Printf("x=%f; x2=%f; v3=%s; m2=%s\n", x, x2, v.String(), m2.String())
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Game) initFbos() {
|
func (g *Game) initFbos() {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user