mirror of
https://github.com/bloeys/nmage.git
synced 2025-12-29 13:28:20 +00:00
Initial uniform buffers implementation
This commit is contained in:
@ -1,23 +1,22 @@
|
|||||||
package buffers
|
package buffers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/bloeys/nmage/assert"
|
"github.com/bloeys/nmage/assert"
|
||||||
"github.com/go-gl/gl/v4.1-core/gl"
|
"github.com/go-gl/gl/v4.1-core/gl"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Element represents an element that makes up a buffer (e.g. Vec3 at an offset of 12 bytes)
|
// Element represents an element that makes up a buffer (e.g. Vec3 at an offset of 12 bytes)
|
||||||
type Element struct {
|
type Element struct {
|
||||||
Offset int
|
Offset int
|
||||||
ElementType
|
ElementType
|
||||||
}
|
}
|
||||||
|
|
||||||
//ElementType is the type of an element thats makes up a buffer (e.g. Vec3)
|
// ElementType is the type of an element thats makes up a buffer (e.g. Vec3)
|
||||||
type ElementType int
|
type ElementType uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DataTypeUnknown ElementType = iota
|
DataTypeUnknown ElementType = iota
|
||||||
|
|
||||||
DataTypeUint32
|
DataTypeUint32
|
||||||
DataTypeInt32
|
DataTypeInt32
|
||||||
DataTypeFloat32
|
DataTypeFloat32
|
||||||
@ -25,35 +24,48 @@ const (
|
|||||||
DataTypeVec2
|
DataTypeVec2
|
||||||
DataTypeVec3
|
DataTypeVec3
|
||||||
DataTypeVec4
|
DataTypeVec4
|
||||||
|
|
||||||
|
DataTypeMat2
|
||||||
|
DataTypeMat3
|
||||||
|
DataTypeMat4
|
||||||
)
|
)
|
||||||
|
|
||||||
func (dt ElementType) GLType() uint32 {
|
func (dt ElementType) GLType() uint32 {
|
||||||
|
|
||||||
switch dt {
|
switch dt {
|
||||||
|
|
||||||
case DataTypeUint32:
|
case DataTypeUint32:
|
||||||
return gl.UNSIGNED_INT
|
return gl.UNSIGNED_INT
|
||||||
case DataTypeInt32:
|
case DataTypeInt32:
|
||||||
return gl.INT
|
return gl.INT
|
||||||
|
|
||||||
case DataTypeFloat32:
|
case DataTypeFloat32:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|
||||||
case DataTypeVec2:
|
case DataTypeVec2:
|
||||||
fallthrough
|
fallthrough
|
||||||
case DataTypeVec3:
|
case DataTypeVec3:
|
||||||
fallthrough
|
fallthrough
|
||||||
case DataTypeVec4:
|
case DataTypeVec4:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat2:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat3:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat4:
|
||||||
return gl.FLOAT
|
return gl.FLOAT
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt))
|
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//CompSize returns the size in bytes for one component of the type (e.g. for Vec2 its 4)
|
// CompSize returns the size in bytes for one component of the type (e.g. for Vec2 its 4).
|
||||||
|
// Bools return 1, although in layout=std140 its 4
|
||||||
func (dt ElementType) CompSize() int32 {
|
func (dt ElementType) CompSize() int32 {
|
||||||
|
|
||||||
switch dt {
|
switch dt {
|
||||||
|
|
||||||
case DataTypeUint32:
|
case DataTypeUint32:
|
||||||
fallthrough
|
fallthrough
|
||||||
case DataTypeFloat32:
|
case DataTypeFloat32:
|
||||||
@ -65,15 +77,21 @@ func (dt ElementType) CompSize() int32 {
|
|||||||
case DataTypeVec3:
|
case DataTypeVec3:
|
||||||
fallthrough
|
fallthrough
|
||||||
case DataTypeVec4:
|
case DataTypeVec4:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat2:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat3:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat4:
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt))
|
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//CompCount returns the number of components in the element (e.g. for Vec2 its 2)
|
// CompCount returns the number of components in the element (e.g. for Vec2 its 2)
|
||||||
func (dt ElementType) CompCount() int32 {
|
func (dt ElementType) CompCount() int32 {
|
||||||
|
|
||||||
switch dt {
|
switch dt {
|
||||||
@ -91,16 +109,24 @@ func (dt ElementType) CompCount() int32 {
|
|||||||
case DataTypeVec4:
|
case DataTypeVec4:
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
|
case DataTypeMat2:
|
||||||
|
return 2 * 2
|
||||||
|
case DataTypeMat3:
|
||||||
|
return 3 * 3
|
||||||
|
case DataTypeMat4:
|
||||||
|
return 4 * 4
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt))
|
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Size returns the total size in bytes (e.g. for vec3 its 3*4=12 bytes)
|
// Size returns the total size in bytes (e.g. for vec3 its 3*4=12 bytes)
|
||||||
func (dt ElementType) Size() int32 {
|
func (dt ElementType) Size() int32 {
|
||||||
|
|
||||||
switch dt {
|
switch dt {
|
||||||
|
|
||||||
case DataTypeUint32:
|
case DataTypeUint32:
|
||||||
fallthrough
|
fallthrough
|
||||||
case DataTypeFloat32:
|
case DataTypeFloat32:
|
||||||
@ -115,8 +141,110 @@ func (dt ElementType) Size() int32 {
|
|||||||
case DataTypeVec4:
|
case DataTypeVec4:
|
||||||
return 4 * 4
|
return 4 * 4
|
||||||
|
|
||||||
|
case DataTypeMat2:
|
||||||
|
return 2 * 2 * 4
|
||||||
|
case DataTypeMat3:
|
||||||
|
return 3 * 3 * 4
|
||||||
|
case DataTypeMat4:
|
||||||
|
return 4 * 4 * 4
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert.T(false, fmt.Sprintf("Unknown data type passed. DataType '%v'", dt))
|
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dt ElementType) GlStd140BaseAlignment() uint8 {
|
||||||
|
|
||||||
|
switch dt {
|
||||||
|
|
||||||
|
case DataTypeUint32:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeFloat32:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeInt32:
|
||||||
|
return 4
|
||||||
|
|
||||||
|
case DataTypeVec2:
|
||||||
|
return 4 * 2
|
||||||
|
|
||||||
|
// Vec3 has the same alignment as vec4
|
||||||
|
case DataTypeVec3:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeVec4:
|
||||||
|
return 4 * 4
|
||||||
|
|
||||||
|
// Matrices follow: (vec4Alignment) * numColumns
|
||||||
|
case DataTypeMat2:
|
||||||
|
return (4 * 4) * 2
|
||||||
|
case DataTypeMat3:
|
||||||
|
return (4 * 4) * 3
|
||||||
|
case DataTypeMat4:
|
||||||
|
return (4 * 4) * 4
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dt ElementType) GlStd140AlignmentBoundary() uint16 {
|
||||||
|
|
||||||
|
switch dt {
|
||||||
|
|
||||||
|
case DataTypeUint32:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeFloat32:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeInt32:
|
||||||
|
return 4
|
||||||
|
|
||||||
|
case DataTypeVec2:
|
||||||
|
return 8
|
||||||
|
|
||||||
|
case DataTypeVec3:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeVec4:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat2:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat3:
|
||||||
|
fallthrough
|
||||||
|
case DataTypeMat4:
|
||||||
|
return 16
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dt ElementType) String() string {
|
||||||
|
|
||||||
|
switch dt {
|
||||||
|
|
||||||
|
case DataTypeUint32:
|
||||||
|
return "uint32"
|
||||||
|
case DataTypeFloat32:
|
||||||
|
return "float32"
|
||||||
|
case DataTypeInt32:
|
||||||
|
return "int32"
|
||||||
|
|
||||||
|
case DataTypeVec2:
|
||||||
|
return "Vec2"
|
||||||
|
case DataTypeVec3:
|
||||||
|
return "Vec3"
|
||||||
|
case DataTypeVec4:
|
||||||
|
return "Vec4"
|
||||||
|
|
||||||
|
case DataTypeMat2:
|
||||||
|
return "Mat2"
|
||||||
|
case DataTypeMat3:
|
||||||
|
return "Mat3"
|
||||||
|
case DataTypeMat4:
|
||||||
|
return "Mat4"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
159
buffers/uniform_buffer.go
Executable file
159
buffers/uniform_buffer.go
Executable file
@ -0,0 +1,159 @@
|
|||||||
|
package buffers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bloeys/gglm/gglm"
|
||||||
|
"github.com/bloeys/nmage/assert"
|
||||||
|
"github.com/bloeys/nmage/logging"
|
||||||
|
"github.com/go-gl/gl/v4.1-core/gl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UniformBufferFieldInput struct {
|
||||||
|
Id uint16
|
||||||
|
Type ElementType
|
||||||
|
}
|
||||||
|
|
||||||
|
type UniformBufferField struct {
|
||||||
|
Id uint16
|
||||||
|
AlignedOffset uint16
|
||||||
|
Type ElementType
|
||||||
|
}
|
||||||
|
|
||||||
|
type UniformBuffer struct {
|
||||||
|
Id uint32
|
||||||
|
// Size is the allocated memory in bytes on the GPU for this uniform buffer
|
||||||
|
Size uint32
|
||||||
|
Fields []UniformBufferField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) Bind() {
|
||||||
|
gl.BindBuffer(gl.UNIFORM_BUFFER, ub.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) UnBind() {
|
||||||
|
gl.BindBuffer(gl.UNIFORM_BUFFER, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) addFields(fields []UniformBufferFieldInput) (totalSize uint32) {
|
||||||
|
|
||||||
|
if len(fields) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var alignedOffset uint16 = 0
|
||||||
|
ub.Fields = make([]UniformBufferField, 0, len(fields))
|
||||||
|
fieldIdToTypeMap := make(map[uint16]ElementType, len(fields))
|
||||||
|
|
||||||
|
for i := 0; i < len(fields); i++ {
|
||||||
|
|
||||||
|
f := fields[i]
|
||||||
|
|
||||||
|
existingFieldType, ok := fieldIdToTypeMap[f.Id]
|
||||||
|
assert.T(!ok, "Uniform buffer field id is reused within the same uniform buffer. FieldId=%d was first used on a field with type=%s and then used on a different field with type=%s\n", f.Id, existingFieldType.String(), f.Type.String())
|
||||||
|
|
||||||
|
// To understand this take an example. Say we have a total offset of 100 and we are adding a vec4.
|
||||||
|
// Vec4s must be aligned to a 16 byte boundary but 100 is not (100 % 16 != 0).
|
||||||
|
//
|
||||||
|
// To fix this, we take the alignment error which is alignErr=100 % 16=4, but this is error to the nearest
|
||||||
|
// boundary, which is below the offset.
|
||||||
|
//
|
||||||
|
// To get the nearest boundary larger than the offset we can:
|
||||||
|
// offset + (boundary - alignErr) == 100 + (16 - 4) == 112; 112 % 16 == 0, meaning its a boundary
|
||||||
|
alignmentBoundary := f.Type.GlStd140AlignmentBoundary()
|
||||||
|
alignmentError := alignedOffset % alignmentBoundary
|
||||||
|
if alignmentError != 0 {
|
||||||
|
alignedOffset += alignmentBoundary - alignmentError
|
||||||
|
}
|
||||||
|
|
||||||
|
newField := UniformBufferField{Id: f.Id, Type: f.Type, AlignedOffset: alignedOffset}
|
||||||
|
ub.Fields = append(ub.Fields, newField)
|
||||||
|
|
||||||
|
// Prepare aligned offset for the next field
|
||||||
|
alignedOffset = newField.AlignedOffset + uint16(f.Type.GlStd140BaseAlignment())
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint32(alignedOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) getField(fieldId uint16, fieldType ElementType) UniformBufferField {
|
||||||
|
|
||||||
|
for i := 0; i < len(ub.Fields); i++ {
|
||||||
|
|
||||||
|
f := ub.Fields[i]
|
||||||
|
|
||||||
|
if f.Id != fieldId {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.T(f.Type == fieldType, "Uniform buffer field id is reused within the same uniform buffer. FieldId=%d was first used on a field with type=%v, but is now being used on a field with type=%v\n", fieldId, f.Type.String(), fieldType.String())
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.ErrLog.Panicf("couldn't find uniform buffer field of id=%d and type=%s\n", fieldId, fieldType.String())
|
||||||
|
return UniformBufferField{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetInt32(fieldId uint16, val int32) {
|
||||||
|
|
||||||
|
f := ub.getField(fieldId, DataTypeInt32)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4, gl.Ptr(&val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetUint32(fieldId uint16, val uint32) {
|
||||||
|
|
||||||
|
f := ub.getField(fieldId, DataTypeUint32)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4, gl.Ptr(&val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetFloat32(fieldId uint16, val float32) {
|
||||||
|
|
||||||
|
f := ub.getField(fieldId, DataTypeFloat32)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4, gl.Ptr(&val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetVec2(fieldId uint16, val *gglm.Vec2) {
|
||||||
|
f := ub.getField(fieldId, DataTypeVec2)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*2, gl.Ptr(&val.Data[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetVec3(fieldId uint16, val *gglm.Vec3) {
|
||||||
|
f := ub.getField(fieldId, DataTypeVec3)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*3, gl.Ptr(&val.Data[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetVec4(fieldId uint16, val *gglm.Vec4) {
|
||||||
|
f := ub.getField(fieldId, DataTypeVec4)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*4, gl.Ptr(&val.Data[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetMat2(fieldId uint16, val *gglm.Mat2) {
|
||||||
|
f := ub.getField(fieldId, DataTypeMat2)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*4, gl.Ptr(&val.Data[0][0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetMat3(fieldId uint16, val *gglm.Mat3) {
|
||||||
|
f := ub.getField(fieldId, DataTypeMat3)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*9, gl.Ptr(&val.Data[0][0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub *UniformBuffer) SetMat4(fieldId uint16, val *gglm.Mat4) {
|
||||||
|
f := ub.getField(fieldId, DataTypeMat4)
|
||||||
|
gl.BufferSubData(gl.UNIFORM_BUFFER, int(f.AlignedOffset), 4*16, gl.Ptr(&val.Data[0][0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUniformBuffer(fields []UniformBufferFieldInput) UniformBuffer {
|
||||||
|
|
||||||
|
ub := UniformBuffer{}
|
||||||
|
|
||||||
|
ub.Size = ub.addFields(fields)
|
||||||
|
|
||||||
|
gl.GenBuffers(1, &ub.Id)
|
||||||
|
if ub.Id == 0 {
|
||||||
|
logging.ErrLog.Panicln("Failed to create OpenGL buffer for a uniform buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
ub.Bind()
|
||||||
|
gl.BufferData(gl.UNIFORM_BUFFER, int(ub.Size), gl.Ptr(nil), gl.STATIC_DRAW)
|
||||||
|
|
||||||
|
return ub
|
||||||
|
}
|
||||||
@ -55,7 +55,7 @@ func NewVertexBuffer(layout ...Element) VertexBuffer {
|
|||||||
|
|
||||||
gl.GenBuffers(1, &vb.Id)
|
gl.GenBuffers(1, &vb.Id)
|
||||||
if vb.Id == 0 {
|
if vb.Id == 0 {
|
||||||
logging.ErrLog.Println("Failed to create OpenGL buffer")
|
logging.ErrLog.Panicln("Failed to create OpenGL buffer")
|
||||||
}
|
}
|
||||||
|
|
||||||
vb.SetLayout(layout...)
|
vb.SetLayout(layout...)
|
||||||
|
|||||||
39
main.go
39
main.go
@ -40,9 +40,10 @@ import (
|
|||||||
- HDR ✅
|
- HDR ✅
|
||||||
- Fix bad point light acne ✅
|
- Fix bad point light acne ✅
|
||||||
- UBO support
|
- UBO support
|
||||||
- Cascaded shadow mapping
|
|
||||||
- Skeletal animations
|
- Skeletal animations
|
||||||
|
- Cascaded shadow mapping
|
||||||
- In some cases we DO want input even when captured by UI. We need two systems within input package, one filtered and one not✅
|
- In some cases we DO want input even when captured by UI. We need two systems within input package, one filtered and one not✅
|
||||||
|
- Support OpenGL 4.1 and 4.6, and default to 4.6.
|
||||||
- Proper model loading (i.e. load model by reading all its meshes, textures, and so on together)
|
- Proper model loading (i.e. load model by reading all its meshes, textures, and so on together)
|
||||||
- Renderer batching
|
- Renderer batching
|
||||||
- Scene graph
|
- Scene graph
|
||||||
@ -668,8 +669,44 @@ func (g *Game) Init() {
|
|||||||
// Initial camera update
|
// Initial camera update
|
||||||
cam.Update()
|
cam.Update()
|
||||||
updateAllProjViewMats(cam.ProjMat, cam.ViewMat)
|
updateAllProjViewMats(cam.ProjMat, cam.ViewMat)
|
||||||
|
|
||||||
|
// Ubos
|
||||||
|
// testUbos()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func testUbos() {
|
||||||
|
|
||||||
|
// ubo := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{
|
||||||
|
// {Id: 0, Type: buffers.DataTypeFloat32}, // 04 00
|
||||||
|
// {Id: 1, Type: buffers.DataTypeVec3}, // 16 16
|
||||||
|
// {Id: 2, Type: buffers.DataTypeFloat32}, // 04 32
|
||||||
|
// {Id: 3, Type: buffers.DataTypeMat2}, // 32 48
|
||||||
|
// }) // Total size: 48+32 = 80
|
||||||
|
|
||||||
|
// println("!!!!!!!!!!!!! Id:", ubo.Id, "; Size:", ubo.Size)
|
||||||
|
// fmt.Printf("%+v\n", ubo.Fields)
|
||||||
|
|
||||||
|
// ubo.Bind()
|
||||||
|
// ubo.SetFloat32(0, 99)
|
||||||
|
// ubo.SetFloat32(2, 199)
|
||||||
|
// ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{33, 33, 33}})
|
||||||
|
|
||||||
|
// ubo.SetMat2(3, &gglm.Mat2{Data: [2][2]float32{{1, 3}, {2, 4}}})
|
||||||
|
|
||||||
|
// var v gglm.Vec3
|
||||||
|
// var m2 gglm.Mat2
|
||||||
|
// var x, x2 float32
|
||||||
|
// 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())
|
||||||
|
|
||||||
|
// ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{-123, 33, 33}})
|
||||||
|
// gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 12, gl.Ptr(&v.Data[0]))
|
||||||
|
// }
|
||||||
|
|
||||||
func (g *Game) initFbos() {
|
func (g *Game) initFbos() {
|
||||||
|
|
||||||
// @TODO: Resize window sized fbos on window resize
|
// @TODO: Resize window sized fbos on window resize
|
||||||
|
|||||||
Reference in New Issue
Block a user